@lynx-js/react 0.107.0 → 0.108.0
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/CHANGELOG.md +46 -0
- package/README.md +4 -2
- package/package.json +3 -3
- package/refresh/.turbo/turbo-build.log +1 -1
- package/refresh/package.json +1 -1
- package/runtime/lazy/import.js +7 -1
- package/runtime/lepus/index.js +1 -1
- package/runtime/lepus/jsx-dev-runtime/index.js +1 -1
- package/runtime/lib/backgroundSnapshot.d.ts +1 -1
- package/runtime/lib/backgroundSnapshot.js +7 -4
- package/runtime/lib/backgroundSnapshot.js.map +1 -1
- package/runtime/lib/document.d.ts +33 -2
- package/runtime/lib/document.js +9 -1
- package/runtime/lib/document.js.map +1 -1
- package/runtime/lib/hydrate.js +2 -1
- package/runtime/lib/hydrate.js.map +1 -1
- package/runtime/lib/lifecycle/delayUnmount.d.ts +1 -1
- package/runtime/lib/lifecycle/delayUnmount.js +1 -4
- package/runtime/lib/lifecycle/delayUnmount.js.map +1 -1
- package/runtime/lib/lifecycle/destroy.js +2 -2
- package/runtime/lib/lifecycle/destroy.js.map +1 -1
- package/runtime/lib/lifecycle/patch/commit.d.ts +21 -3
- package/runtime/lib/lifecycle/patch/commit.js +44 -56
- package/runtime/lib/lifecycle/patch/commit.js.map +1 -1
- package/runtime/lib/lifecycle/patch/snapshotPatch.d.ts +5 -0
- package/runtime/lib/lifecycle/patch/snapshotPatch.js +5 -0
- package/runtime/lib/lifecycle/patch/snapshotPatch.js.map +1 -1
- package/runtime/lib/lifecycle/patch/snapshotPatchApply.d.ts +5 -0
- package/runtime/lib/lifecycle/patch/snapshotPatchApply.js +21 -5
- package/runtime/lib/lifecycle/patch/snapshotPatchApply.js.map +1 -1
- package/runtime/lib/lifecycle/reload.js +9 -4
- package/runtime/lib/lifecycle/reload.js.map +1 -1
- package/runtime/lib/lifecycle/render.d.ts +1 -3
- package/runtime/lib/lifecycle/render.js +4 -6
- package/runtime/lib/lifecycle/render.js.map +1 -1
- package/runtime/lib/list.d.ts +1 -1
- package/runtime/lib/list.js +60 -23
- package/runtime/lib/list.js.map +1 -1
- package/runtime/lib/lynx/env.d.ts +1 -1
- package/runtime/lib/lynx/env.js +1 -1
- package/runtime/lib/lynx/env.js.map +1 -1
- package/runtime/lib/lynx/performance.js +7 -1
- package/runtime/lib/lynx/performance.js.map +1 -1
- package/runtime/lib/lynx/runWithForce.js +12 -4
- package/runtime/lib/lynx/runWithForce.js.map +1 -1
- package/runtime/lib/lynx/tt.js +6 -8
- package/runtime/lib/lynx/tt.js.map +1 -1
- package/runtime/lib/lynx-api.d.ts +4 -4
- package/runtime/lib/lynx-api.js +5 -5
- package/runtime/lib/lynx-api.js.map +1 -1
- package/runtime/lib/lynx.js +6 -5
- package/runtime/lib/lynx.js.map +1 -1
- package/runtime/lib/opcodes.js +2 -2
- package/runtime/lib/opcodes.js.map +1 -1
- package/runtime/lib/renderToOpcodes/index.js +5 -1
- package/runtime/lib/renderToOpcodes/index.js.map +1 -1
- package/runtime/lib/root.d.ts +4 -0
- package/runtime/lib/root.js +6 -2
- package/runtime/lib/root.js.map +1 -1
- package/runtime/lib/snapshot/ref.d.ts +1 -1
- package/runtime/lib/snapshot/ref.js +1 -1
- package/runtime/lib/snapshot/ref.js.map +1 -1
- package/runtime/lib/snapshot/spread.d.ts +8 -2
- package/runtime/lib/snapshot/spread.js +12 -6
- package/runtime/lib/snapshot/spread.js.map +1 -1
- package/runtime/lib/snapshot.d.ts +31 -5
- package/runtime/lib/snapshot.js +15 -3
- package/runtime/lib/snapshot.js.map +1 -1
- package/runtime/src/backgroundSnapshot.ts +20 -13
- package/runtime/src/document.ts +33 -2
- package/runtime/src/hydrate.ts +3 -1
- package/runtime/src/lifecycle/delayUnmount.ts +2 -2
- package/runtime/src/lifecycle/destroy.ts +3 -2
- package/runtime/src/lifecycle/patch/commit.ts +75 -72
- package/runtime/src/lifecycle/patch/snapshotPatch.ts +9 -0
- package/runtime/src/lifecycle/patch/snapshotPatchApply.ts +20 -5
- package/runtime/src/lifecycle/reload.ts +12 -4
- package/runtime/src/lifecycle/render.ts +6 -8
- package/runtime/src/list.ts +71 -23
- package/runtime/src/lynx/env.ts +1 -1
- package/runtime/src/lynx/performance.ts +6 -1
- package/runtime/src/lynx/runWithForce.ts +17 -6
- package/runtime/src/lynx/tt.ts +10 -17
- package/runtime/src/lynx-api.ts +7 -7
- package/runtime/src/lynx.ts +7 -6
- package/runtime/src/opcodes.ts +2 -2
- package/runtime/src/renderToOpcodes/index.ts +6 -1
- package/runtime/src/root.ts +6 -2
- package/runtime/src/snapshot/ref.ts +5 -5
- package/runtime/src/snapshot/spread.ts +14 -6
- package/runtime/src/snapshot.ts +35 -10
- package/testing-library/README.md +1 -1
- package/testing-library/dist/env/vitest.js +23 -23
- package/testing-library/dist/index.d.ts +24 -24
- package/testing-library/dist/index.js +2 -2
- package/testing-library/dist/pure.js +13 -15
- package/testing-library/dist/vitest-global-setup.js +13 -14
- package/testing-library/types/entry.d.ts +2 -2
- package/testing-library/types/index.d.ts +3 -3
- package/transform/dist/wasm.cjs +1 -1
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
// Copyright 2024 The Lynx Authors. All rights reserved.
|
|
2
2
|
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Implements the reload (thinking of "refresh" in browser) for both main thread
|
|
7
|
+
* and background thread.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { render } from 'preact';
|
|
11
|
+
|
|
4
12
|
import { hydrate } from '../hydrate.js';
|
|
5
13
|
import { LifecycleConstant } from '../lifecycleConstant.js';
|
|
6
14
|
import { __pendingListUpdates } from '../list.js';
|
|
7
15
|
import { __root, setRoot } from '../root.js';
|
|
8
|
-
import { takeGlobalRefPatchMap } from '../snapshot/ref.js';
|
|
9
16
|
import { SnapshotInstance, __page, snapshotInstanceManager } from '../snapshot.js';
|
|
17
|
+
import { takeGlobalRefPatchMap } from '../snapshot/ref.js';
|
|
10
18
|
import { isEmptyObject } from '../utils.js';
|
|
11
|
-
import { destroyBackground } from './destroy.js';
|
|
12
19
|
import { destroyWorklet } from '../worklet/destroy.js';
|
|
20
|
+
import { destroyBackground } from './destroy.js';
|
|
13
21
|
import { clearJSReadyEventIdSwap, isJSReady } from './event/jsReady.js';
|
|
14
22
|
import { increaseReloadVersion } from './pass.js';
|
|
15
23
|
import { deinitGlobalSnapshotPatch } from './patch/snapshotPatch.js';
|
|
16
|
-
import {
|
|
24
|
+
import { renderMainThread } from './render.js';
|
|
17
25
|
|
|
18
26
|
function reloadMainThread(data: any, options: UpdatePageOption): void {
|
|
19
27
|
if (__PROFILE__) {
|
|
@@ -74,7 +82,7 @@ function reloadBackground(updateData: Record<string, any>): void {
|
|
|
74
82
|
// COW when modify `lynx.__initData` to make sure Provider & Consumer works
|
|
75
83
|
lynx.__initData = Object.assign({}, lynx.__initData, updateData);
|
|
76
84
|
|
|
77
|
-
|
|
85
|
+
render(__root.__jsx, __root as any);
|
|
78
86
|
|
|
79
87
|
if (__PROFILE__) {
|
|
80
88
|
console.profileEnd();
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
// Copyright 2024 The Lynx Authors. All rights reserved.
|
|
2
2
|
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Implements the IFR (Instant First-Frame Rendering) on main thread.
|
|
7
|
+
*/
|
|
8
|
+
|
|
4
9
|
import { render } from 'preact';
|
|
5
|
-
import type { ComponentChild, ContainerNode } from 'preact';
|
|
6
10
|
|
|
7
11
|
import { renderOpcodesInto } from '../opcodes.js';
|
|
8
12
|
import { render as renderToString } from '../renderToOpcodes/index.js';
|
|
9
13
|
import { __root } from '../root.js';
|
|
10
|
-
import { commitToMainThread } from './patch/commit.js';
|
|
11
14
|
|
|
12
15
|
function renderMainThread(): void {
|
|
13
16
|
/* v8 ignore start */
|
|
@@ -47,9 +50,4 @@ function renderMainThread(): void {
|
|
|
47
50
|
/* v8 ignore stop */
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
render(vnode, parent);
|
|
52
|
-
void commitToMainThread();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export { renderMainThread, renderBackground };
|
|
53
|
+
export { renderMainThread };
|
package/runtime/src/list.ts
CHANGED
|
@@ -78,10 +78,12 @@ export class ListUpdateInfoRecording implements ListUpdateInfo {
|
|
|
78
78
|
// __FlushElementTree(listElement);
|
|
79
79
|
// });
|
|
80
80
|
__SetAttribute(listElement, 'update-list-info', this.__toAttribute());
|
|
81
|
+
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(this.list.childNodes);
|
|
81
82
|
__UpdateListCallbacks(
|
|
82
83
|
listElement,
|
|
83
|
-
|
|
84
|
+
componentAtIndex,
|
|
84
85
|
enqueueComponentFactory(),
|
|
86
|
+
componentAtIndexes,
|
|
85
87
|
);
|
|
86
88
|
}
|
|
87
89
|
|
|
@@ -243,13 +245,17 @@ export function clearListGlobal(): void {
|
|
|
243
245
|
}
|
|
244
246
|
}
|
|
245
247
|
|
|
246
|
-
export function componentAtIndexFactory(
|
|
248
|
+
export function componentAtIndexFactory(
|
|
249
|
+
ctx: SnapshotInstance[],
|
|
250
|
+
): [ComponentAtIndexCallback, ComponentAtIndexesCallback] {
|
|
247
251
|
const componentAtIndex = (
|
|
248
252
|
list: FiberElement,
|
|
249
253
|
listID: number,
|
|
250
254
|
cellIndex: number,
|
|
251
255
|
operationID: number,
|
|
252
256
|
enableReuseNotification: boolean,
|
|
257
|
+
enableBatchRender: boolean = false,
|
|
258
|
+
asyncFlush: boolean = false,
|
|
253
259
|
) => {
|
|
254
260
|
const signMap = gSignMap[listID];
|
|
255
261
|
const recycleMap = gRecycleMap[listID];
|
|
@@ -284,7 +290,14 @@ export function componentAtIndexFactory(ctx: SnapshotInstance[]): ComponentAtInd
|
|
|
284
290
|
if (recycleSignMap?.has(sign)) {
|
|
285
291
|
signMap.set(sign, childCtx);
|
|
286
292
|
recycleSignMap.delete(sign);
|
|
287
|
-
|
|
293
|
+
if (!enableBatchRender) {
|
|
294
|
+
__FlushElementTree(root, { triggerLayout: true, operationID, elementID: sign, listID });
|
|
295
|
+
} else if (enableBatchRender && asyncFlush) {
|
|
296
|
+
__FlushElementTree(root, { asyncFlush: true });
|
|
297
|
+
} else {
|
|
298
|
+
// enableBatchRender == true && asyncFlush == false
|
|
299
|
+
// in this case, no need to invoke __FlushElementTree because in the end of componentAtIndexes(), the list will invoke __FlushElementTree.
|
|
300
|
+
}
|
|
288
301
|
return sign;
|
|
289
302
|
} else {
|
|
290
303
|
const newCtx = childCtx.takeElements();
|
|
@@ -299,24 +312,31 @@ export function componentAtIndexFactory(ctx: SnapshotInstance[]): ComponentAtInd
|
|
|
299
312
|
hydrate(oldCtx, childCtx);
|
|
300
313
|
oldCtx.unRenderElements();
|
|
301
314
|
const root = childCtx.__element_root!;
|
|
302
|
-
if (
|
|
303
|
-
|
|
315
|
+
if (!enableBatchRender) {
|
|
316
|
+
const flushOptions: FlushOptions = {
|
|
304
317
|
triggerLayout: true,
|
|
305
318
|
operationID,
|
|
306
319
|
elementID: sign,
|
|
307
320
|
listID,
|
|
308
|
-
|
|
321
|
+
};
|
|
322
|
+
if (enableReuseNotification) {
|
|
323
|
+
flushOptions.listReuseNotification = {
|
|
309
324
|
listElement: list,
|
|
310
325
|
itemKey: platformInfo['item-key'],
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
__FlushElementTree(root, flushOptions);
|
|
329
|
+
} else if (enableBatchRender && asyncFlush) {
|
|
330
|
+
const flushOptions: FlushOptions = {
|
|
331
|
+
asyncFlush: true,
|
|
332
|
+
};
|
|
333
|
+
if (enableReuseNotification) {
|
|
334
|
+
flushOptions.listReuseNotification = {
|
|
335
|
+
listElement: list,
|
|
336
|
+
itemKey: platformInfo['item-key'],
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
__FlushElementTree(root, flushOptions);
|
|
320
340
|
}
|
|
321
341
|
signMap.set(sign, childCtx);
|
|
322
342
|
commitMainThreadPatchUpdate(undefined);
|
|
@@ -327,17 +347,43 @@ export function componentAtIndexFactory(ctx: SnapshotInstance[]): ComponentAtInd
|
|
|
327
347
|
const root = childCtx.__element_root!;
|
|
328
348
|
__AppendElement(list, root);
|
|
329
349
|
const sign = __GetElementUniqueID(root);
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
350
|
+
if (!enableBatchRender) {
|
|
351
|
+
__FlushElementTree(root, {
|
|
352
|
+
triggerLayout: true,
|
|
353
|
+
operationID,
|
|
354
|
+
elementID: sign,
|
|
355
|
+
listID,
|
|
356
|
+
});
|
|
357
|
+
} else if (enableBatchRender && asyncFlush) {
|
|
358
|
+
__FlushElementTree(root, {
|
|
359
|
+
asyncFlush: true,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
336
362
|
signMap.set(sign, childCtx);
|
|
337
363
|
commitMainThreadPatchUpdate(undefined);
|
|
338
364
|
return sign;
|
|
339
365
|
};
|
|
340
|
-
|
|
366
|
+
|
|
367
|
+
const componentAtIndexes = (
|
|
368
|
+
list: FiberElement,
|
|
369
|
+
listID: number,
|
|
370
|
+
cellIndexes: number[],
|
|
371
|
+
operationIDs: number[],
|
|
372
|
+
enableReuseNotification: boolean,
|
|
373
|
+
asyncFlush: boolean,
|
|
374
|
+
) => {
|
|
375
|
+
const uiSigns = cellIndexes.map((cellIndex, index) => {
|
|
376
|
+
const operationID = operationIDs[index] ?? 0;
|
|
377
|
+
return componentAtIndex(list, listID, cellIndex, operationID, enableReuseNotification, true, asyncFlush);
|
|
378
|
+
});
|
|
379
|
+
__FlushElementTree(list, {
|
|
380
|
+
triggerLayout: true,
|
|
381
|
+
operationIDs: operationIDs,
|
|
382
|
+
elementIDs: uiSigns,
|
|
383
|
+
listID,
|
|
384
|
+
});
|
|
385
|
+
};
|
|
386
|
+
return [componentAtIndex, componentAtIndexes] as const;
|
|
341
387
|
}
|
|
342
388
|
|
|
343
389
|
export function enqueueComponentFactory(): EnqueueComponentCallback {
|
|
@@ -371,11 +417,13 @@ export function snapshotCreateList(
|
|
|
371
417
|
): FiberElement {
|
|
372
418
|
const signMap = new Map<number, SnapshotInstance>();
|
|
373
419
|
const recycleMap = new Map<string, Map<number, SnapshotInstance>>();
|
|
420
|
+
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory([]);
|
|
374
421
|
const list = __CreateList(
|
|
375
422
|
pageId,
|
|
376
|
-
|
|
423
|
+
componentAtIndex,
|
|
377
424
|
enqueueComponentFactory(),
|
|
378
425
|
{},
|
|
426
|
+
componentAtIndexes,
|
|
379
427
|
);
|
|
380
428
|
const listID = __GetElementUniqueID(list);
|
|
381
429
|
gSignMap[listID] = signMap;
|
package/runtime/src/lynx/env.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
4
|
import type { DataProcessorDefinition } from '../lynx-api.js';
|
|
5
5
|
|
|
6
|
-
export function
|
|
6
|
+
export function setupLynxTestingEnv(): void {
|
|
7
7
|
if (!__LEPUS__) {
|
|
8
8
|
const { initData, updateData } = lynxCoreInject.tt._params;
|
|
9
9
|
// @ts-ignore
|
|
@@ -6,6 +6,7 @@ import type { VNode } from 'preact';
|
|
|
6
6
|
|
|
7
7
|
import { DIFF } from '../renderToOpcodes/constants.js';
|
|
8
8
|
import { __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js';
|
|
9
|
+
import { isSdkVersionGt } from '../utils.js';
|
|
9
10
|
|
|
10
11
|
enum PerformanceTimingKeys {
|
|
11
12
|
updateSetStateTrigger,
|
|
@@ -93,7 +94,11 @@ function beginPipeline(needTimestamps: boolean, pipelineOrigin: PipelineOrigin,
|
|
|
93
94
|
break;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
if (isSdkVersionGt(3, 0)) {
|
|
98
|
+
lynx.performance?._onPipelineStart?.(globalPipelineOptions.pipelineID, globalPipelineOptions);
|
|
99
|
+
} else {
|
|
100
|
+
lynx.performance?._onPipelineStart?.(globalPipelineOptions.pipelineID);
|
|
101
|
+
}
|
|
97
102
|
if (timingFlag) {
|
|
98
103
|
lynx.performance?._bindPipelineIdWithTimingFlag?.(globalPipelineOptions.pipelineID, timingFlag);
|
|
99
104
|
}
|
|
@@ -2,13 +2,17 @@ import { options } from 'preact';
|
|
|
2
2
|
import type { VNode } from 'preact';
|
|
3
3
|
import { COMPONENT, DIFF, DIFFED, FORCE } from '../renderToOpcodes/constants.js';
|
|
4
4
|
|
|
5
|
+
const sForcedVNode = Symbol('FORCE');
|
|
6
|
+
|
|
7
|
+
type PatchedVNode = VNode & { [sForcedVNode]?: true };
|
|
8
|
+
|
|
5
9
|
export function runWithForce(cb: () => void): void {
|
|
6
10
|
// save vnode and its `_component` in WeakMap
|
|
7
11
|
const m = new WeakMap<VNode, any>();
|
|
8
12
|
|
|
9
13
|
const oldDiff = options[DIFF];
|
|
10
14
|
|
|
11
|
-
options[DIFF] = (vnode:
|
|
15
|
+
options[DIFF] = (vnode: PatchedVNode) => {
|
|
12
16
|
if (oldDiff) {
|
|
13
17
|
oldDiff(vnode);
|
|
14
18
|
}
|
|
@@ -28,19 +32,26 @@ export function runWithForce(cb: () => void): void {
|
|
|
28
32
|
return m.get(vnode);
|
|
29
33
|
},
|
|
30
34
|
});
|
|
35
|
+
vnode[sForcedVNode] = true;
|
|
31
36
|
};
|
|
32
37
|
|
|
33
38
|
const oldDiffed = options[DIFFED];
|
|
34
39
|
|
|
35
|
-
options[DIFFED] = (vnode:
|
|
40
|
+
options[DIFFED] = (vnode: PatchedVNode) => {
|
|
36
41
|
if (oldDiffed) {
|
|
37
42
|
oldDiffed(vnode);
|
|
38
43
|
}
|
|
39
44
|
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
//
|
|
43
|
-
vnode[
|
|
45
|
+
// There would be cases when `options[DIFF]` has been reset while options[DIFFED] is not,
|
|
46
|
+
// so we need to check if `vnode` is patched by `options[DIFF]`.
|
|
47
|
+
// We only want to change the patched vnode
|
|
48
|
+
if (vnode[sForcedVNode]) {
|
|
49
|
+
// delete is a reverse operation of previous `Object.defineProperty`
|
|
50
|
+
delete vnode[COMPONENT];
|
|
51
|
+
delete vnode[sForcedVNode];
|
|
52
|
+
// restore
|
|
53
|
+
vnode[COMPONENT] = m.get(vnode);
|
|
54
|
+
}
|
|
44
55
|
};
|
|
45
56
|
|
|
46
57
|
try {
|
package/runtime/src/lynx/tt.ts
CHANGED
|
@@ -1,34 +1,30 @@
|
|
|
1
1
|
// Copyright 2024 The Lynx Authors. All rights reserved.
|
|
2
2
|
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
import { render } from 'preact';
|
|
5
|
+
|
|
4
6
|
import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js';
|
|
5
7
|
import {
|
|
6
|
-
PerformanceTimingKeys,
|
|
7
8
|
PerformanceTimingFlags,
|
|
9
|
+
PerformanceTimingKeys,
|
|
8
10
|
PipelineOrigins,
|
|
9
11
|
beginPipeline,
|
|
10
12
|
markTiming,
|
|
11
13
|
} from './performance.js';
|
|
12
14
|
import { BackgroundSnapshotInstance, hydrate } from '../backgroundSnapshot.js';
|
|
15
|
+
import { runWithForce } from './runWithForce.js';
|
|
13
16
|
import { destroyBackground } from '../lifecycle/destroy.js';
|
|
14
17
|
import { delayedEvents, delayedPublishEvent } from '../lifecycle/event/delayEvents.js';
|
|
15
18
|
import { delayLifecycleEvent, delayedLifecycleEvents } from '../lifecycle/event/delayLifecycleEvents.js';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
commitPatchUpdate,
|
|
19
|
-
genCommitTaskId,
|
|
20
|
-
globalCommitTaskMap,
|
|
21
|
-
patchesToCommit,
|
|
22
|
-
type PatchList,
|
|
23
|
-
} from '../lifecycle/patch/commit.js';
|
|
19
|
+
import { commitPatchUpdate, genCommitTaskId, globalCommitTaskMap } from '../lifecycle/patch/commit.js';
|
|
20
|
+
import type { PatchList } from '../lifecycle/patch/commit.js';
|
|
24
21
|
import { reloadBackground } from '../lifecycle/reload.js';
|
|
25
|
-
import { renderBackground } from '../lifecycle/render.js';
|
|
26
22
|
import { CHILDREN } from '../renderToOpcodes/constants.js';
|
|
27
23
|
import { __root } from '../root.js';
|
|
28
24
|
import { globalRefsToSet, updateBackgroundRefs } from '../snapshot/ref.js';
|
|
29
25
|
import { backgroundSnapshotInstanceManager } from '../snapshot.js';
|
|
30
26
|
import { destroyWorklet } from '../worklet/destroy.js';
|
|
31
|
-
|
|
27
|
+
|
|
32
28
|
export { runWithForce };
|
|
33
29
|
|
|
34
30
|
function injectTt(): void {
|
|
@@ -129,14 +125,11 @@ function onLifecycleEventImpl(type: string, data: any): void {
|
|
|
129
125
|
console.profile('commitChanges');
|
|
130
126
|
}
|
|
131
127
|
const commitTaskId = genCommitTaskId();
|
|
132
|
-
patchesToCommit.push(
|
|
133
|
-
{ snapshotPatch, id: commitTaskId },
|
|
134
|
-
);
|
|
135
128
|
const patchList: PatchList = {
|
|
136
|
-
patchList:
|
|
129
|
+
patchList: [{ snapshotPatch, id: commitTaskId }],
|
|
137
130
|
};
|
|
138
|
-
clearPatchesToCommit();
|
|
139
131
|
const obj = commitPatchUpdate(patchList, { isHydration: true });
|
|
132
|
+
|
|
140
133
|
lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
|
|
141
134
|
updateBackgroundRefs(commitTaskId);
|
|
142
135
|
globalCommitTaskMap.forEach((commitTask, id) => {
|
|
@@ -212,7 +205,7 @@ function updateGlobalProps(newData: Record<string, any>): void {
|
|
|
212
205
|
// This is already done because updateFromRoot will consume all dirty flags marked by
|
|
213
206
|
// the setState, and setState's flush will be a noop. No extra diffs will be needed.
|
|
214
207
|
Promise.resolve().then(() => {
|
|
215
|
-
runWithForce(() =>
|
|
208
|
+
runWithForce(() => render(__root.__jsx, __root as any));
|
|
216
209
|
});
|
|
217
210
|
lynxCoreInject.tt.GlobalEventEmitter.emit('onGlobalPropsChanged');
|
|
218
211
|
}
|
package/runtime/src/lynx-api.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// Copyright 2024 The Lynx Authors. All rights reserved.
|
|
2
2
|
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
import { render } from 'preact';
|
|
4
5
|
import { createContext, createElement } from 'preact/compat';
|
|
5
6
|
import { useState } from 'preact/hooks';
|
|
6
7
|
import type { Consumer, FC, ReactNode } from 'react';
|
|
7
8
|
|
|
8
9
|
import { factory, withInitDataInState } from './compat/initData.js';
|
|
9
10
|
import { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js';
|
|
10
|
-
import { renderBackground } from './lifecycle/render.js';
|
|
11
11
|
import { LifecycleConstant } from './lifecycleConstant.js';
|
|
12
12
|
import { flushDelayedLifecycleEvents } from './lynx/tt.js';
|
|
13
13
|
import { __root } from './root.js';
|
|
@@ -43,13 +43,13 @@ export interface Root {
|
|
|
43
43
|
* return <view>...</view>
|
|
44
44
|
* }
|
|
45
45
|
*
|
|
46
|
-
* if (
|
|
46
|
+
* if (__MAIN_THREAD__) {
|
|
47
47
|
* root.render(
|
|
48
48
|
* <DataProvider data={DEFAULT_DATA}>
|
|
49
49
|
* <App/>
|
|
50
50
|
* </DataProvider>
|
|
51
51
|
* );
|
|
52
|
-
* } else if (
|
|
52
|
+
* } else if (__BACKGROUND__) {
|
|
53
53
|
* fetchData().then((data) => {
|
|
54
54
|
* root.render(
|
|
55
55
|
* <DataProvider data={data}>
|
|
@@ -82,11 +82,11 @@ export interface Root {
|
|
|
82
82
|
*/
|
|
83
83
|
export const root: Root = {
|
|
84
84
|
render: (jsx: ReactNode): void => {
|
|
85
|
-
if (
|
|
85
|
+
if (__MAIN_THREAD__) {
|
|
86
86
|
__root.__jsx = jsx;
|
|
87
87
|
} else {
|
|
88
88
|
__root.__jsx = jsx;
|
|
89
|
-
|
|
89
|
+
render(jsx, __root as any);
|
|
90
90
|
if (__FIRST_SCREEN_SYNC_TIMING__ === 'immediately') {
|
|
91
91
|
// This is for cases where `root.render()` is called asynchronously,
|
|
92
92
|
// `firstScreen` message might have been reached.
|
|
@@ -370,7 +370,7 @@ export interface Lynx {
|
|
|
370
370
|
registerDataProcessors: (dataProcessorDefinition?: DataProcessorDefinition) => void;
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
export {
|
|
373
|
+
export { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js';
|
|
374
374
|
export { runOnBackground } from './worklet/runOnBackground.js';
|
|
375
|
+
export { runOnMainThread } from './worklet/runOnMainThread.js';
|
|
375
376
|
export { MainThreadRef, useMainThreadRef } from './worklet/workletRef.js';
|
|
376
|
-
export { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js';
|
package/runtime/src/lynx.ts
CHANGED
|
@@ -11,21 +11,21 @@ import { initDelayUnmount } from './lifecycle/delayUnmount.js';
|
|
|
11
11
|
import { replaceCommitHook, replaceRequestAnimationFrame } from './lifecycle/patch/commit.js';
|
|
12
12
|
import { injectUpdateMainThread } from './lifecycle/patch/updateMainThread.js';
|
|
13
13
|
import { injectCalledByNative } from './lynx/calledByNative.js';
|
|
14
|
-
import {
|
|
14
|
+
import { setupLynxTestingEnv } from './lynx/env.js';
|
|
15
15
|
import { injectLepusMethods } from './lynx/injectLepusMethods.js';
|
|
16
16
|
import { initTimingAPI } from './lynx/performance.js';
|
|
17
17
|
import { injectTt } from './lynx/tt.js';
|
|
18
18
|
export { runWithForce } from './lynx/runWithForce.js';
|
|
19
19
|
|
|
20
20
|
// @ts-expect-error Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature
|
|
21
|
-
if (
|
|
21
|
+
if (__MAIN_THREAD__ && typeof globalThis.processEvalResult === 'undefined') {
|
|
22
22
|
// @ts-expect-error Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature
|
|
23
23
|
globalThis.processEvalResult = <T>(result: ((schema: string) => T) | undefined, schema: string) => {
|
|
24
24
|
return result?.(schema);
|
|
25
25
|
};
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
if (
|
|
28
|
+
if (__MAIN_THREAD__) {
|
|
29
29
|
injectCalledByNative();
|
|
30
30
|
injectUpdateMainThread();
|
|
31
31
|
if (__DEV__) {
|
|
@@ -39,8 +39,9 @@ if (__PROFILE__) {
|
|
|
39
39
|
initProfileHook();
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
if (
|
|
43
|
-
|
|
42
|
+
if (__BACKGROUND__) {
|
|
43
|
+
// Trick Preact and TypeScript to accept our custom document adapter.
|
|
44
|
+
options.document = document as any;
|
|
44
45
|
setupBackgroundDocument();
|
|
45
46
|
injectTt();
|
|
46
47
|
|
|
@@ -53,4 +54,4 @@ if (__JS__) {
|
|
|
53
54
|
}
|
|
54
55
|
}
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
setupLynxTestingEnv();
|
package/runtime/src/opcodes.ts
CHANGED
|
@@ -52,7 +52,7 @@ export function ssrHydrateByOpcodes(
|
|
|
52
52
|
const signMap = gSignMap[listElementUniqueID] = new Map();
|
|
53
53
|
gRecycleMap[listElementUniqueID] = new Map();
|
|
54
54
|
const enqueueFunc = enqueueComponentFactory();
|
|
55
|
-
const componentAtIndex = componentAtIndexFactory(top.childNodes);
|
|
55
|
+
const [componentAtIndex, componentAtIndexes] = componentAtIndexFactory(top.childNodes);
|
|
56
56
|
for (const child of top.childNodes) {
|
|
57
57
|
if (child.__element_root) {
|
|
58
58
|
const childElementUniqueID = __GetElementUniqueID(child.__element_root);
|
|
@@ -64,7 +64,7 @@ export function ssrHydrateByOpcodes(
|
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
|
-
__UpdateListCallbacks(listElement, componentAtIndex, enqueueFunc);
|
|
67
|
+
__UpdateListCallbacks(listElement, componentAtIndex, enqueueFunc, componentAtIndexes);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
stack.pop();
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Implements rendering to opcodes.
|
|
7
|
+
* This module is modified from preact-render-to-string@6.0.3 to generate
|
|
8
|
+
* opcodes instead of HTML strings for Lynx.
|
|
9
|
+
*/
|
|
10
|
+
|
|
6
11
|
// @ts-nocheck
|
|
7
12
|
|
|
8
13
|
import { Fragment, h, options } from 'preact';
|
package/runtime/src/root.ts
CHANGED
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
import { BackgroundSnapshotInstance } from './backgroundSnapshot.js';
|
|
5
5
|
import { SnapshotInstance } from './snapshot.js';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* The internal ReactLynx's root.
|
|
9
|
+
* {@link @lynx-js/react!Root | root}.
|
|
10
|
+
*/
|
|
7
11
|
let __root: (SnapshotInstance | BackgroundSnapshotInstance) & { __jsx?: React.ReactNode; __opcodes?: any[] };
|
|
8
12
|
|
|
9
13
|
function setRoot(root: typeof __root): void {
|
|
10
14
|
__root = root;
|
|
11
15
|
}
|
|
12
16
|
|
|
13
|
-
if (
|
|
17
|
+
if (__MAIN_THREAD__) {
|
|
14
18
|
setRoot(new SnapshotInstance('root'));
|
|
15
|
-
} else if (
|
|
19
|
+
} else if (__BACKGROUND__) {
|
|
16
20
|
setRoot(new BackgroundSnapshotInstance('root'));
|
|
17
21
|
}
|
|
18
22
|
|
|
@@ -137,12 +137,12 @@ function markRefToRemove(sign: string, ref: unknown): void {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
export {
|
|
140
|
-
updateRef,
|
|
141
|
-
takeGlobalRefPatchMap,
|
|
142
|
-
updateBackgroundRefs,
|
|
143
|
-
unref,
|
|
144
|
-
transformRef,
|
|
145
140
|
globalRefsToRemove,
|
|
146
141
|
globalRefsToSet,
|
|
147
142
|
markRefToRemove,
|
|
143
|
+
takeGlobalRefPatchMap,
|
|
144
|
+
transformRef,
|
|
145
|
+
unref,
|
|
146
|
+
updateBackgroundRefs,
|
|
147
|
+
updateRef,
|
|
148
148
|
};
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
// Copyright 2024 The Lynx Authors. All rights reserved.
|
|
2
2
|
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Handles JSX spread operator in the snapshot system.
|
|
7
|
+
*
|
|
8
|
+
* Spread operators in JSX (e.g., <div {...props}>) are transformed into
|
|
9
|
+
* optimized attribute updates at compile time, avoiding runtime object spreads.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { BackgroundSnapshotInstance } from '../backgroundSnapshot.js';
|
|
13
|
+
import { __pendingListUpdates, ListUpdateInfoRecording } from '../list.js';
|
|
4
14
|
import { SnapshotInstance } from '../snapshot.js';
|
|
15
|
+
import { isDirectOrDeepEqual, isEmptyObject, pick } from '../utils.js';
|
|
5
16
|
import { updateEvent } from './event.js';
|
|
6
|
-
import {
|
|
17
|
+
import { updateGesture } from './gesture.js';
|
|
18
|
+
import { platformInfoAttributes, updateListItemPlatformInfo } from './platformInfo.js';
|
|
7
19
|
import { transformRef, updateRef } from './ref.js';
|
|
8
20
|
import { updateWorkletEvent } from './workletEvent.js';
|
|
9
21
|
import { updateWorkletRef } from './workletRef.js';
|
|
10
|
-
import { updateGesture } from './gesture.js';
|
|
11
|
-
import { platformInfoAttributes, updateListItemPlatformInfo } from './platformInfo.js';
|
|
12
|
-
import { isDirectOrDeepEqual, isEmptyObject, pick } from '../utils.js';
|
|
13
|
-
import { __pendingListUpdates, ListUpdateInfoRecording } from '../list.js';
|
|
14
22
|
|
|
15
23
|
const eventRegExp = /^(([A-Za-z-]*):)?(bind|catch|capture-bind|capture-catch|global-bind)([A-Za-z]+)$/;
|
|
16
24
|
const eventTypeMap: Record<string, string> = {
|
|
@@ -284,4 +292,4 @@ function transformSpread(
|
|
|
284
292
|
return result;
|
|
285
293
|
}
|
|
286
294
|
|
|
287
|
-
export {
|
|
295
|
+
export { transformSpread, updateSpread };
|
package/runtime/src/snapshot.ts
CHANGED
|
@@ -1,26 +1,44 @@
|
|
|
1
1
|
// Copyright 2024 The Lynx Authors. All rights reserved.
|
|
2
2
|
// Licensed under the Apache License Version 2.0 that can be found in the
|
|
3
3
|
// LICENSE file in the root directory of this source tree.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Core snapshot system that implements a compiler-hinted virtual DOM.
|
|
7
|
+
*
|
|
8
|
+
* Key components:
|
|
9
|
+
* 1. {@link Snapshot}: Template definition generated at compile time
|
|
10
|
+
* 2. {@link SnapshotInstance}: Runtime instance in the main thread
|
|
11
|
+
* 3. {@link BackgroundSnapshotInstance}: Runtime instance in the background thread
|
|
12
|
+
*
|
|
13
|
+
* The system uses static analysis to identify dynamic parts and generate
|
|
14
|
+
* optimized update instructions, avoiding full virtual DOM diffing.
|
|
15
|
+
*/
|
|
16
|
+
|
|
4
17
|
import type { Worklet, WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
|
|
5
18
|
|
|
6
19
|
import type { BackgroundSnapshotInstance } from './backgroundSnapshot.js';
|
|
20
|
+
import { SnapshotOperation, __globalSnapshotPatch } from './lifecycle/patch/snapshotPatch.js';
|
|
7
21
|
import { ListUpdateInfoRecording, __pendingListUpdates, snapshotDestroyList } from './list.js';
|
|
8
22
|
import { unref } from './snapshot/ref.js';
|
|
9
|
-
import { SnapshotOperation, __globalSnapshotPatch } from './lifecycle/patch/snapshotPatch.js';
|
|
10
23
|
import { isDirectOrDeepEqual } from './utils.js';
|
|
11
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Types of dynamic parts that can be updated in a snapshot
|
|
27
|
+
* These are determined at compile time through static analysis
|
|
28
|
+
*/
|
|
12
29
|
export const enum DynamicPartType {
|
|
13
|
-
Attr = 0,
|
|
14
|
-
Spread,
|
|
15
|
-
Slot,
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// Used by compat layer
|
|
21
|
-
MultiChildren,
|
|
30
|
+
Attr = 0, // Regular attribute updates
|
|
31
|
+
Spread, // Spread operator in JSX
|
|
32
|
+
Slot, // Slot for component children
|
|
33
|
+
Children, // Regular children updates
|
|
34
|
+
ListChildren, // List/array children updates
|
|
35
|
+
MultiChildren, // Multiple children updates (compat layer)
|
|
22
36
|
}
|
|
23
37
|
|
|
38
|
+
/**
|
|
39
|
+
* A snapshot definition that contains all the information needed to create and update elements
|
|
40
|
+
* This is generated at compile time through static analysis of the JSX
|
|
41
|
+
*/
|
|
24
42
|
interface Snapshot {
|
|
25
43
|
create: null | ((ctx: SnapshotInstance) => FiberElement[]);
|
|
26
44
|
update: null | ((ctx: SnapshotInstance, index: number, oldValue: any) => void)[];
|
|
@@ -234,6 +252,13 @@ export interface SerializedSnapshotInstance {
|
|
|
234
252
|
const DEFAULT_ENTRY_NAME = '__Card__';
|
|
235
253
|
const DEFAULT_CSS_ID = 0;
|
|
236
254
|
|
|
255
|
+
/**
|
|
256
|
+
* The runtime instance of a {@link Snapshot} on the main thread that manages
|
|
257
|
+
* the actual elements and handles updates to dynamic parts.
|
|
258
|
+
*
|
|
259
|
+
* This class is designed to be compatible with Preact's {@link ContainerNode}
|
|
260
|
+
* interface for Preact's renderer to operate upon.
|
|
261
|
+
*/
|
|
237
262
|
export class SnapshotInstance {
|
|
238
263
|
__id: number;
|
|
239
264
|
__snapshot_def: Snapshot;
|
|
@@ -58,7 +58,7 @@ test('renders options.wrapper around node', async () => {
|
|
|
58
58
|
});
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
💡 Since our testing environment (`@lynx-js/
|
|
61
|
+
💡 Since our testing environment (`@lynx-js/testing-environment`) is based on jsdom, You may also be interested in installing `@testing-library/jest-dom` so you can use
|
|
62
62
|
[the custom jest matchers](https://github.com/testing-library/jest-dom).
|
|
63
63
|
|
|
64
64
|
## Examples
|