@lwrjs/client-modules 0.20.3 → 0.20.5
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.
- package/build/bundle/prod/lwr/init/init.js +1 -1
- package/build/es/modules/lwr/init/init.js +4 -33
- package/build/es/modules/lwr/scheduler/scheduler.d.ts +21 -0
- package/build/es/modules/lwr/scheduler/scheduler.js +66 -0
- package/build/modules/lwr/init/init.js +4 -40
- package/build/modules/lwr/scheduler/scheduler.js +85 -0
- package/package.json +6 -5
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{BOOTSTRAP_END as e,INIT as
|
|
1
|
+
import{BOOTSTRAP_END as e,INIT as t,INIT_MODULE as o}from"lwr/metrics";import{logOperationStart as n,logOperationEnd as i}from"lwr/profiler";import{hydrateComponent as r,createElement as s}from"lwc";let c=0;async function l(){const e=function(e){if(!globalThis.performance||!function(){const e=globalThis,{SSREnabled:t}=e.LWR&&e.LWR.env||{};return!!t}())return{shouldYield:!1,timeOfLastYield:e};const t=50,o=globalThis.performance.now();if(o-e>t)return{shouldYield:!0,timeOfLastYield:o};return{shouldYield:!1,timeOfLastYield:e}}(c);e.shouldYield&&(c=e.timeOfLastYield,await async function(){const e=globalThis.scheduler;return function(e){let t,o=e[0],n=1;for(;n<e.length;){const i=e[n],r=e[n+1];if(n+=2,("optionalAccess"===i||"optionalCall"===i)&&null==o)return;"access"===i||"optionalAccess"===i?(t=o,o=r(o)):"call"!==i&&"optionalCall"!==i||(o=r(((...e)=>o.call(t,...e))),t=void 0)}return o}([e,"optionalAccess",e=>e.yield])?e.yield():new Promise((e=>setTimeout(e,0)))}())}const a="lwr:hydrate",d="visible";function f(e,t,o){r(e,t,o)}function p(e,t,o){const n=m||(m=new IntersectionObserver(((e,t)=>{e.forEach((e=>{if(e.isIntersecting){const o=e.target;t.unobserve(o);const n=u.get(o);if(n){u.delete(o);const{ctor:e,props:t}=n;f(o,e,t)}}}))}),{root:null,rootMargin:"100px"}),m);u.set(e,{ctor:t,props:o}),n.observe(e)}const u=new Map;let m;function h(e){return"IntersectionObserver"in globalThis&&e.getAttribute(a)===d}function b(e,t){return s(e,{is:t})}function g(e){return e.replace(/\/v\/[a-zA-Z0-9-_.]+$/,"").replace("/","-").replace(/([A-Z])/g,(e=>`-${e.toLowerCase()}`))}const w=/-([a-z])/g;function v(e){return e.replace(w,(e=>e[1].toUpperCase()))}function y(r,s={}){void 0!==globalThis.customElements&&void 0!==globalThis.document?(n({id:t}),(async()=>{let e=0;const t=globalThis.document;for(const[c,a]of r){await l();const r=++e,d=g(c);if(!t.body.querySelector(d)){n({id:o,specifier:c,specifierIndex:r});const e=b(d,a),s=t.querySelector("[lwr-root]");s?s.appendChild(e):t.body.appendChild(e),i({id:o,specifier:c,specifierIndex:r,metadata:{renderMode:"spa"}});continue}const u=t.querySelectorAll(d);for(const e of u){n({id:o,specifier:c,specifierIndex:r});const t=e.dataset.lwrPropsId;if(t){h(e)?p(e,a,s[t]||{}):f(e,a,s[t]||{}),i({id:o,specifier:c,specifierIndex:r,metadata:{renderMode:"ssr"}});continue}const l=b(d,a);for(const{name:t,value:o}of e.attributes){l.setAttribute(t,o);const e=v(t);e in l&&(l[e]=o)}for(;e.childNodes.length>0;)l.appendChild(e.childNodes[0]);const u=e.parentElement;u&&u.replaceChild(l,e),i({id:o,specifier:c,specifierIndex:r,metadata:{renderMode:"csr"}})}}})(),i({id:t}),n({id:e})):n({id:e})}export{a as HYDRATE_DIRECTIVE,d as HYDRATE_VISIBLE_VALUE,v as getPropFromAttrName,y as init,g as toKebabCase};
|
|
2
2
|
//# sourceMappingURL=init.js.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BOOTSTRAP_END, INIT, INIT_MODULE } from 'lwr/metrics';
|
|
2
2
|
import { logOperationStart, logOperationEnd } from 'lwr/profiler';
|
|
3
|
+
import { yieldIfNecessary } from 'lwr/scheduler';
|
|
3
4
|
// TODO: This is a temporal workaround until https://github.com/salesforce/lwc/pull/2083 is sorted - tmp
|
|
4
5
|
// eslint-disable-next-line lwr/only-allowed-imports
|
|
5
6
|
import { createElement } from 'lwc';
|
|
@@ -77,28 +78,6 @@ function createVisibilityObserver() {
|
|
|
77
78
|
});
|
|
78
79
|
return visibilityObserver;
|
|
79
80
|
}
|
|
80
|
-
const shouldYield = (() => {
|
|
81
|
-
const globalThisLWR = globalThis;
|
|
82
|
-
const { SSREnabled } = (globalThisLWR.LWR && globalThisLWR.LWR.env) || {};
|
|
83
|
-
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
84
|
-
if (!globalThis.performance || !SSREnabled) {
|
|
85
|
-
return () => false;
|
|
86
|
-
}
|
|
87
|
-
// Break up hydration tasks into timed batches.
|
|
88
|
-
// Borrowed from https://tinyurl.com/5b4fw7eb
|
|
89
|
-
const TASK_BATCH_DURATION = 50;
|
|
90
|
-
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
91
|
-
let timeOfLastYield = globalThis.performance.now();
|
|
92
|
-
return () => {
|
|
93
|
-
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
94
|
-
const now = globalThis.performance.now();
|
|
95
|
-
if (now - timeOfLastYield > TASK_BATCH_DURATION) {
|
|
96
|
-
timeOfLastYield = now;
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
100
|
-
};
|
|
101
|
-
})();
|
|
102
81
|
function initializeWebComponent(elementName, Ctor) {
|
|
103
82
|
return createElement(elementName, { is: Ctor });
|
|
104
83
|
}
|
|
@@ -145,11 +124,9 @@ export function init(rootModules, serverData = {}) {
|
|
|
145
124
|
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
146
125
|
const document = globalThis.document;
|
|
147
126
|
for (const [specifier, ctor] of rootModules) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
await yieldToMainThread();
|
|
152
|
-
}
|
|
127
|
+
// Yield to the main thread during long hydration tasks
|
|
128
|
+
// eslint-disable-next-line no-await-in-loop
|
|
129
|
+
await yieldIfNecessary();
|
|
153
130
|
const specifierIndex = ++index;
|
|
154
131
|
const elementName = toKebabCase(specifier);
|
|
155
132
|
// initialize and inject the root module into the LWR Root or DOM if it is missing
|
|
@@ -229,10 +206,4 @@ export function init(rootModules, serverData = {}) {
|
|
|
229
206
|
logOperationEnd({ id: INIT });
|
|
230
207
|
logOperationStart({ id: BOOTSTRAP_END });
|
|
231
208
|
}
|
|
232
|
-
// Allows the browser to yield to the main thread during long-running tasks, improving responsiveness.
|
|
233
|
-
async function yieldToMainThread() {
|
|
234
|
-
const scheduler = globalThis.scheduler;
|
|
235
|
-
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
236
|
-
return scheduler?.yield ? scheduler.yield() : new Promise((resolve) => setTimeout(resolve, 0));
|
|
237
|
-
}
|
|
238
209
|
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of checking whether to yield to the main thread
|
|
3
|
+
*/
|
|
4
|
+
export interface ShouldYieldResult {
|
|
5
|
+
shouldYield: boolean;
|
|
6
|
+
timeOfLastYield: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Reset the internal yield tracking state. Used primarily for testing.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export declare function resetYieldTracking(): void;
|
|
13
|
+
/**
|
|
14
|
+
* Yields control to the main thread if enough time has elapsed since the last yield.
|
|
15
|
+
* This automatically tracks yield timing internally to break up long-running tasks
|
|
16
|
+
* into batches, improving responsiveness without requiring manual state management.
|
|
17
|
+
*
|
|
18
|
+
* @returns Promise that resolves after yielding (if necessary) to the main thread
|
|
19
|
+
*/
|
|
20
|
+
export declare function yieldIfNecessary(): Promise<void>;
|
|
21
|
+
//# sourceMappingURL=scheduler.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Track the time of last yield for scheduler batching
|
|
2
|
+
let timeOfLastYield = 0;
|
|
3
|
+
/**
|
|
4
|
+
* Reset the internal yield tracking state. Used primarily for testing.
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export function resetYieldTracking() {
|
|
8
|
+
timeOfLastYield = 0;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Yields control to the main thread if enough time has elapsed since the last yield.
|
|
12
|
+
* This automatically tracks yield timing internally to break up long-running tasks
|
|
13
|
+
* into batches, improving responsiveness without requiring manual state management.
|
|
14
|
+
*
|
|
15
|
+
* @returns Promise that resolves after yielding (if necessary) to the main thread
|
|
16
|
+
*/
|
|
17
|
+
export async function yieldIfNecessary() {
|
|
18
|
+
const result = checkShouldYield(timeOfLastYield);
|
|
19
|
+
if (result.shouldYield) {
|
|
20
|
+
timeOfLastYield = result.timeOfLastYield;
|
|
21
|
+
await yieldToMainThread();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Checks if the current execution should yield to the main thread based on elapsed time.
|
|
26
|
+
* Break up long-running tasks into timed batches to improve responsiveness.
|
|
27
|
+
* Borrowed from https://tinyurl.com/5b4fw7eb
|
|
28
|
+
*
|
|
29
|
+
* @param timeOfLastYield - Timestamp of the last yield (from performance.now())
|
|
30
|
+
* @returns Object containing whether to yield and the updated timestamp
|
|
31
|
+
*/
|
|
32
|
+
function checkShouldYield(timeOfLastYield) {
|
|
33
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
34
|
+
if (!globalThis.performance || !getSSREnabled()) {
|
|
35
|
+
return { shouldYield: false, timeOfLastYield };
|
|
36
|
+
}
|
|
37
|
+
const TASK_BATCH_DURATION = 50;
|
|
38
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
39
|
+
const now = globalThis.performance.now();
|
|
40
|
+
if (now - timeOfLastYield > TASK_BATCH_DURATION) {
|
|
41
|
+
return { shouldYield: true, timeOfLastYield: now };
|
|
42
|
+
}
|
|
43
|
+
return { shouldYield: false, timeOfLastYield };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Yields control to the main thread during long-running tasks to improve responsiveness.
|
|
47
|
+
* Uses the scheduler.yield() API if available, otherwise falls back to setTimeout.
|
|
48
|
+
*
|
|
49
|
+
* @returns Promise that resolves after yielding to the main thread
|
|
50
|
+
*/
|
|
51
|
+
async function yieldToMainThread() {
|
|
52
|
+
const scheduler = globalThis.scheduler;
|
|
53
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
54
|
+
return scheduler?.yield ? scheduler.yield() : new Promise((resolve) => setTimeout(resolve, 0));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Gets the SSREnabled flag from the global LWR environment
|
|
58
|
+
*
|
|
59
|
+
* @returns Whether SSR is enabled
|
|
60
|
+
*/
|
|
61
|
+
function getSSREnabled() {
|
|
62
|
+
const globalThisLWR = globalThis;
|
|
63
|
+
const { SSREnabled } = (globalThisLWR.LWR && globalThisLWR.LWR.env) || {};
|
|
64
|
+
return !!SSREnabled;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=scheduler.js.map
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
// eslint-disable-next-line lwr/only-allowed-type-imports
|
|
2
2
|
|
|
3
|
-
// eslint-disable-next-line lwr/only-allowed-type-imports
|
|
4
|
-
|
|
5
3
|
import { BOOTSTRAP_END, INIT, INIT_MODULE } from 'lwr/metrics';
|
|
6
4
|
import { logOperationStart, logOperationEnd } from 'lwr/profiler';
|
|
5
|
+
import { yieldIfNecessary } from 'lwr/scheduler';
|
|
7
6
|
|
|
8
7
|
// TODO: This is a temporal workaround until https://github.com/salesforce/lwc/pull/2083 is sorted - tmp
|
|
9
8
|
// eslint-disable-next-line lwr/only-allowed-imports
|
|
@@ -97,32 +96,6 @@ function createVisibilityObserver() {
|
|
|
97
96
|
});
|
|
98
97
|
return visibilityObserver;
|
|
99
98
|
}
|
|
100
|
-
const shouldYield = (() => {
|
|
101
|
-
const globalThisLWR = globalThis;
|
|
102
|
-
const {
|
|
103
|
-
SSREnabled
|
|
104
|
-
} = globalThisLWR.LWR && globalThisLWR.LWR.env || {};
|
|
105
|
-
|
|
106
|
-
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
107
|
-
if (!globalThis.performance || !SSREnabled) {
|
|
108
|
-
return () => false;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Break up hydration tasks into timed batches.
|
|
112
|
-
// Borrowed from https://tinyurl.com/5b4fw7eb
|
|
113
|
-
const TASK_BATCH_DURATION = 50;
|
|
114
|
-
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
115
|
-
let timeOfLastYield = globalThis.performance.now();
|
|
116
|
-
return () => {
|
|
117
|
-
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
118
|
-
const now = globalThis.performance.now();
|
|
119
|
-
if (now - timeOfLastYield > TASK_BATCH_DURATION) {
|
|
120
|
-
timeOfLastYield = now;
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
return false;
|
|
124
|
-
};
|
|
125
|
-
})();
|
|
126
99
|
function initializeWebComponent(elementName, Ctor) {
|
|
127
100
|
return createElement(elementName, {
|
|
128
101
|
is: Ctor
|
|
@@ -175,11 +148,9 @@ export function init(rootModules, serverData = {}) {
|
|
|
175
148
|
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
176
149
|
const document = globalThis.document;
|
|
177
150
|
for (const [specifier, ctor] of rootModules) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
await yieldToMainThread();
|
|
182
|
-
}
|
|
151
|
+
// Yield to the main thread during long hydration tasks
|
|
152
|
+
// eslint-disable-next-line no-await-in-loop
|
|
153
|
+
await yieldIfNecessary();
|
|
183
154
|
const specifierIndex = ++index;
|
|
184
155
|
const elementName = toKebabCase(specifier);
|
|
185
156
|
|
|
@@ -286,11 +257,4 @@ export function init(rootModules, serverData = {}) {
|
|
|
286
257
|
logOperationStart({
|
|
287
258
|
id: BOOTSTRAP_END
|
|
288
259
|
});
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Allows the browser to yield to the main thread during long-running tasks, improving responsiveness.
|
|
292
|
-
async function yieldToMainThread() {
|
|
293
|
-
const scheduler = globalThis.scheduler;
|
|
294
|
-
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
295
|
-
return scheduler?.yield ? scheduler.yield() : new Promise(resolve => setTimeout(resolve, 0));
|
|
296
260
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result of checking whether to yield to the main thread
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Track the time of last yield for scheduler batching
|
|
6
|
+
let timeOfLastYield = 0;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Reset the internal yield tracking state. Used primarily for testing.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export function resetYieldTracking() {
|
|
13
|
+
timeOfLastYield = 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Yields control to the main thread if enough time has elapsed since the last yield.
|
|
18
|
+
* This automatically tracks yield timing internally to break up long-running tasks
|
|
19
|
+
* into batches, improving responsiveness without requiring manual state management.
|
|
20
|
+
*
|
|
21
|
+
* @returns Promise that resolves after yielding (if necessary) to the main thread
|
|
22
|
+
*/
|
|
23
|
+
export async function yieldIfNecessary() {
|
|
24
|
+
const result = checkShouldYield(timeOfLastYield);
|
|
25
|
+
if (result.shouldYield) {
|
|
26
|
+
timeOfLastYield = result.timeOfLastYield;
|
|
27
|
+
await yieldToMainThread();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Checks if the current execution should yield to the main thread based on elapsed time.
|
|
33
|
+
* Break up long-running tasks into timed batches to improve responsiveness.
|
|
34
|
+
* Borrowed from https://tinyurl.com/5b4fw7eb
|
|
35
|
+
*
|
|
36
|
+
* @param timeOfLastYield - Timestamp of the last yield (from performance.now())
|
|
37
|
+
* @returns Object containing whether to yield and the updated timestamp
|
|
38
|
+
*/
|
|
39
|
+
function checkShouldYield(timeOfLastYield) {
|
|
40
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
41
|
+
if (!globalThis.performance || !getSSREnabled()) {
|
|
42
|
+
return {
|
|
43
|
+
shouldYield: false,
|
|
44
|
+
timeOfLastYield
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
const TASK_BATCH_DURATION = 50;
|
|
48
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
49
|
+
const now = globalThis.performance.now();
|
|
50
|
+
if (now - timeOfLastYield > TASK_BATCH_DURATION) {
|
|
51
|
+
return {
|
|
52
|
+
shouldYield: true,
|
|
53
|
+
timeOfLastYield: now
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
shouldYield: false,
|
|
58
|
+
timeOfLastYield
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Yields control to the main thread during long-running tasks to improve responsiveness.
|
|
64
|
+
* Uses the scheduler.yield() API if available, otherwise falls back to setTimeout.
|
|
65
|
+
*
|
|
66
|
+
* @returns Promise that resolves after yielding to the main thread
|
|
67
|
+
*/
|
|
68
|
+
async function yieldToMainThread() {
|
|
69
|
+
const scheduler = globalThis.scheduler;
|
|
70
|
+
// eslint-disable-next-line lwr/no-unguarded-apis
|
|
71
|
+
return scheduler?.yield ? scheduler.yield() : new Promise(resolve => setTimeout(resolve, 0));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Gets the SSREnabled flag from the global LWR environment
|
|
76
|
+
*
|
|
77
|
+
* @returns Whether SSR is enabled
|
|
78
|
+
*/
|
|
79
|
+
function getSSREnabled() {
|
|
80
|
+
const globalThisLWR = globalThis;
|
|
81
|
+
const {
|
|
82
|
+
SSREnabled
|
|
83
|
+
} = globalThisLWR.LWR && globalThisLWR.LWR.env || {};
|
|
84
|
+
return !!SSREnabled;
|
|
85
|
+
}
|
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.5",
|
|
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.5"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
|
-
"@lwrjs/types": "0.20.
|
|
40
|
+
"@lwrjs/types": "0.20.5",
|
|
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",
|
|
@@ -61,7 +61,8 @@
|
|
|
61
61
|
"lwr/lockerSandbox",
|
|
62
62
|
"lwr/metrics",
|
|
63
63
|
"lwr/profiler",
|
|
64
|
-
"lwr/serverDataCallback"
|
|
64
|
+
"lwr/serverDataCallback",
|
|
65
|
+
"lwr/scheduler"
|
|
65
66
|
]
|
|
66
67
|
},
|
|
67
68
|
"engines": {
|
|
@@ -70,5 +71,5 @@
|
|
|
70
71
|
"volta": {
|
|
71
72
|
"extends": "../../../package.json"
|
|
72
73
|
},
|
|
73
|
-
"gitHead": "
|
|
74
|
+
"gitHead": "4ee0fa9b14629833090b379589202b9ac5a4a6c3"
|
|
74
75
|
}
|