diff --git a/.changeset/plain-lions-hope.md b/.changeset/plain-lions-hope.md
new file mode 100644
index 0000000000..853d812bb3
--- /dev/null
+++ b/.changeset/plain-lions-hope.md
@@ -0,0 +1,3 @@
+---
+
+---
diff --git a/packages/react/package.json b/packages/react/package.json
index 74cde9de94..e77c7110e8 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -56,12 +56,12 @@
"default": "./runtime/lepus/jsx-runtime/index.js"
},
"./hooks": {
- "types": "./runtime/lib/snapshot/hooks/react.d.ts",
- "default": "./runtime/lib/snapshot/hooks/react.js"
+ "types": "./runtime/lib/core/hooks/react.d.ts",
+ "default": "./runtime/lib/core/hooks/react.js"
},
"./lepus/hooks": {
- "types": "./runtime/lib/snapshot/hooks/react.d.ts",
- "default": "./runtime/lib/snapshot/hooks/mainThread.js"
+ "types": "./runtime/lib/core/hooks/react.d.ts",
+ "default": "./runtime/lib/core/hooks/mainThread.js"
},
"./lepus": {
"types": "./runtime/lepus/index.d.ts",
@@ -132,10 +132,10 @@
"./runtime/lazy/import.d.ts"
],
"hooks": [
- "./runtime/lib/snapshot/hooks/react.d.ts"
+ "./runtime/lib/core/hooks/react.d.ts"
],
"lepus/hooks": [
- "./runtime/lib/snapshot/hooks/react.d.ts"
+ "./runtime/lib/core/hooks/react.d.ts"
],
"internal": [
"./runtime/lib/internal.d.ts"
diff --git a/packages/react/runtime/__test__/snapshot/hooks/mainThread.test.tsx b/packages/react/runtime/__test__/core/hooks/mainThread.test.tsx
similarity index 91%
rename from packages/react/runtime/__test__/snapshot/hooks/mainThread.test.tsx
rename to packages/react/runtime/__test__/core/hooks/mainThread.test.tsx
index 1bd26b8da4..26c0b86cce 100644
--- a/packages/react/runtime/__test__/snapshot/hooks/mainThread.test.tsx
+++ b/packages/react/runtime/__test__/core/hooks/mainThread.test.tsx
@@ -3,15 +3,16 @@
// LICENSE file in the root directory of this source tree.
import { beforeEach, afterEach, vi } from 'vitest';
-import { globalEnvManager } from '../utils/envManager';
+import { globalEnvManager } from '../../snapshot/utils/envManager';
import { describe } from 'vitest';
import { it } from 'vitest';
import { expect } from 'vitest';
import { beforeAll } from 'vitest';
import { replaceCommitHook } from '../../../src/snapshot/lifecycle/patch/commit';
-import { elementTree } from '../utils/nativeMethod';
+import { elementTree } from '../../snapshot/utils/nativeMethod';
import { __root } from '../../../src/root';
import {
+ installMainThreadHooks,
useState,
useMemo,
useEffect,
@@ -23,9 +24,9 @@ import {
useId,
useErrorBoundary,
useContext,
-} from '../../../src/snapshot/hooks/mainThread';
+} from '../../../src/core/hooks/mainThread';
import { options, createContext } from 'preact';
-import { HOOK } from '../../../src/snapshot/renderToOpcodes/constants.js';
+import { DIFF, HOOK } from '../../../src/shared/render-constants.js';
beforeAll(() => {
replaceCommitHook();
@@ -43,6 +44,14 @@ afterEach(() => {
});
describe('mainThread hooks', () => {
+ it('should skip duplicate installation', () => {
+ const diffHook = options[DIFF];
+
+ installMainThreadHooks();
+
+ expect(options[DIFF]).toBe(diffHook);
+ });
+
it('should get initialValue', () => {
let setCount;
options[HOOK] = vi.fn();
diff --git a/packages/react/runtime/__test__/snapshot/hooks/useLynxGlobalEventListener.test.jsx b/packages/react/runtime/__test__/core/hooks/useLynxGlobalEventListener.test.jsx
similarity index 94%
rename from packages/react/runtime/__test__/snapshot/hooks/useLynxGlobalEventListener.test.jsx
rename to packages/react/runtime/__test__/core/hooks/useLynxGlobalEventListener.test.jsx
index 3a1bb319e7..e1e1254070 100644
--- a/packages/react/runtime/__test__/snapshot/hooks/useLynxGlobalEventListener.test.jsx
+++ b/packages/react/runtime/__test__/core/hooks/useLynxGlobalEventListener.test.jsx
@@ -6,7 +6,7 @@
import { EventEmitter } from 'node:events';
import { render } from 'preact';
-import { useState } from 'preact/compat';
+import { useState } from 'preact/hooks';
import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { setupBackgroundDocument } from '../../../src/document';
@@ -17,10 +17,10 @@ import {
SnapshotInstance,
setupPage,
} from '../../../src/snapshot';
-import { backgroundSnapshotInstanceToJSON } from '../utils/debug.js';
-import { elementTree } from '../utils/nativeMethod';
+import { backgroundSnapshotInstanceToJSON } from '../../snapshot/utils/debug.js';
+import { elementTree } from '../../snapshot/utils/nativeMethod';
-import { globalEnvManager } from '../utils/envManager';
+import { globalEnvManager } from '../../snapshot/utils/envManager';
describe('useLynxGlobalEventListener', () => {
/** @type {SnapshotInstance} */
diff --git a/packages/react/runtime/__test__/guardrails/snapshot-containment.test.ts b/packages/react/runtime/__test__/guardrails/snapshot-containment.test.ts
index da3c13192d..d40f4ff672 100644
--- a/packages/react/runtime/__test__/guardrails/snapshot-containment.test.ts
+++ b/packages/react/runtime/__test__/guardrails/snapshot-containment.test.ts
@@ -14,13 +14,14 @@ const reactRoot = path.resolve(runtimeRoot, '..');
const srcRoot = path.join(runtimeRoot, 'src');
const testRoot = path.join(runtimeRoot, '__test__');
const transformRoot = path.join(reactRoot, 'transform');
+const coreRoot = path.join(srcRoot, 'core');
+const sharedRoot = path.join(srcRoot, 'shared');
const legacySourceDirs = [
'alog',
'compat',
'debug',
'gesture',
- 'hooks',
'legacy-react-runtime',
'lifecycle',
'list',
@@ -41,7 +42,6 @@ const containedTestDirs = [
'css',
'debug',
'gesture',
- 'hooks',
'lifecycle',
'lynx',
'utils',
@@ -95,6 +95,7 @@ describe('snapshot containment guardrails', () => {
);
const offenders = walkFiles(srcRoot)
.filter((file) => !file.includes(`${path.sep}src${path.sep}snapshot${path.sep}`))
+ .filter((file) => !file.includes(`${path.sep}src${path.sep}element-template${path.sep}`))
.filter((file) => oldPathPattern.test(fs.readFileSync(file, 'utf8')))
.map((file) => path.relative(runtimeRoot, file));
@@ -112,6 +113,24 @@ describe('snapshot containment guardrails', () => {
expect(offenders).toEqual([]);
});
+ test('prevents core and shared modules from depending on runtime backends', () => {
+ const backendImportPattern =
+ /(?:from\s+|import\s*\(\s*)['"](?:\.\/|\.\.\/)+(?:snapshot|element-template)(?:\/|['"])/;
+ const offenders = [
+ ...walkFiles(coreRoot),
+ ...walkFiles(sharedRoot),
+ ]
+ .filter((file) => backendImportPattern.test(fs.readFileSync(file, 'utf8')))
+ .map((file) => path.relative(runtimeRoot, file));
+
+ expect(offenders).toEqual([]);
+ });
+
+ test('allows ReactLynx hooks to live under core instead of snapshot', () => {
+ expect(fs.existsSync(path.join(srcRoot, 'snapshot', 'hooks'))).toBe(false);
+ expect(fs.existsSync(path.join(srcRoot, 'core', 'hooks'))).toBe(true);
+ });
+
test('prevents transform output from referencing untracked old runtime source paths', () => {
const oldPackageSourcePattern = new RegExp(
String.raw`@lynx-js/react/src/(?:${legacySourceDirPattern})(?:/|['"])`,
@@ -136,7 +155,7 @@ describe('snapshot containment guardrails', () => {
expect(typesVersions).not.toContain('./runtime/lib/hooks/');
expect(typesVersions).not.toContain('./runtime/lib/legacy-react-runtime/');
- expect(typesVersions).toContain('./runtime/lib/snapshot/hooks/react.d.ts');
+ expect(typesVersions).toContain('./runtime/lib/core/hooks/react.d.ts');
expect(typesVersions).toContain(
'./runtime/lib/snapshot/legacy-react-runtime/index.d.ts',
);
diff --git a/packages/react/runtime/__test__/snapshot/debug/profile-module.test.ts b/packages/react/runtime/__test__/shared/profile.test.ts
similarity index 90%
rename from packages/react/runtime/__test__/snapshot/debug/profile-module.test.ts
rename to packages/react/runtime/__test__/shared/profile.test.ts
index a0d549d0b5..a39b8c4577 100644
--- a/packages/react/runtime/__test__/snapshot/debug/profile-module.test.ts
+++ b/packages/react/runtime/__test__/shared/profile.test.ts
@@ -11,7 +11,7 @@ type LynxLike = {
performance?: PerformanceLike;
};
-describe('debug/profile module', () => {
+describe('shared/profile module', () => {
let originalLynx: LynxLike;
let originalProfileFlag: unknown;
@@ -37,7 +37,7 @@ describe('debug/profile module', () => {
performance: perf,
};
- const profile = await import('../../../src/snapshot/debug/profile');
+ const profile = await import('../../src/shared/profile');
expect(profile.isProfiling).toBe(true);
});
@@ -53,7 +53,7 @@ describe('debug/profile module', () => {
performance: perf,
};
- const profile = await import('../../../src/snapshot/debug/profile');
+ const profile = await import('../../src/shared/profile');
expect(profile.isProfiling).toBe(false);
expect(() => profile.profileStart('trace')).not.toThrow();
@@ -75,7 +75,7 @@ describe('debug/profile module', () => {
performance: perf,
};
- const profile = await import('../../../src/snapshot/debug/profile');
+ const profile = await import('../../src/shared/profile');
profile.profileStart('trace-name', { args: { foo: 'bar' } });
profile.profileEnd();
diff --git a/packages/react/runtime/__test__/snapshot/debug/backgroundSnapshot-profile.test.jsx b/packages/react/runtime/__test__/snapshot/debug/backgroundSnapshot-profile.test.jsx
index 11043bfb2d..3a12947223 100644
--- a/packages/react/runtime/__test__/snapshot/debug/backgroundSnapshot-profile.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/debug/backgroundSnapshot-profile.test.jsx
@@ -9,7 +9,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it } from 'vitest';
import { setupDocument } from '../../../src/document';
import { setupVNodeSourceHook } from '../../../src/snapshot/debug/vnodeSource';
import { SnapshotOperation, SnapshotOperationParams } from '../../../src/snapshot/lifecycle/patch/snapshotPatch';
-import { DIFFED, DOM } from '../../../src/snapshot/renderToOpcodes/constants';
+import { DIFFED, DOM } from '../../../src/shared/render-constants';
import { __root } from '../../../src/root';
import {
setupPage,
diff --git a/packages/react/runtime/__test__/snapshot/debug/profile.test.jsx b/packages/react/runtime/__test__/snapshot/debug/profile.test.jsx
index 50e96f4516..5969efc828 100644
--- a/packages/react/runtime/__test__/snapshot/debug/profile.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/debug/profile.test.jsx
@@ -10,16 +10,7 @@ import { setupDocument } from '../../../src/document';
import { setupPage, snapshotInstanceManager } from '../../../src/snapshot';
import { initProfileHook } from '../../../src/snapshot/debug/profileHooks';
import { useState } from '../../../src/index';
-import {
- COMPONENT,
- DIFF,
- DIFF2,
- DIFFED,
- HOOKS,
- LIST,
- RENDER,
- VNODE,
-} from '../../../src/snapshot/renderToOpcodes/constants';
+import { COMPONENT, DIFF, DIFF2, DIFFED, HOOKS, LIST, RENDER, VNODE } from '../../../src/shared/render-constants';
describe('profile', () => {
let scratch;
diff --git a/packages/react/runtime/__test__/snapshot/debug/react-hooks-profile.test.jsx b/packages/react/runtime/__test__/snapshot/debug/react-hooks-profile.test.jsx
index 14cd6f6a91..4c28389610 100644
--- a/packages/react/runtime/__test__/snapshot/debug/react-hooks-profile.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/debug/react-hooks-profile.test.jsx
@@ -3,7 +3,6 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
*/
-import { render } from 'preact';
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { setupDocument } from '../../../src/document';
@@ -11,12 +10,26 @@ import { setupPage, snapshotInstanceManager } from '../../../src/snapshot';
import { elementTree } from '../utils/nativeMethod';
import { globalEnvManager } from '../utils/envManager';
+let currentRender;
+let currentCreateElement;
+
async function importHooksWithProfileRecording(isRecording) {
const original = lynx.performance.isProfileRecording;
lynx.performance.isProfileRecording = vi.fn(() => isRecording);
vi.resetModules();
try {
- return await import('../../../src/snapshot/hooks/react');
+ const [hooks, preact, document, utils] = await Promise.all([
+ import('../../../src/core/hooks/react'),
+ import('preact'),
+ import('../../../src/document'),
+ import('../../../src/utils'),
+ ]);
+ document.setupBackgroundDocument();
+ preact.options.document = document.document;
+ preact.options.requestAnimationFrame = utils.lynxQueueMicrotask;
+ currentRender = preact.render;
+ currentCreateElement = preact.h;
+ return hooks;
} finally {
lynx.performance.isProfileRecording = original;
}
@@ -37,7 +50,9 @@ describe('react hooks profile', () => {
});
afterEach(() => {
- render(null, scratch);
+ currentRender?.(null, scratch);
+ currentRender = undefined;
+ currentCreateElement = undefined;
elementTree.clear();
vi.restoreAllMocks();
});
@@ -52,16 +67,16 @@ describe('react hooks profile', () => {
useLayoutEffect(() => {
return () => {};
}, []);
- return ;
+ return currentCreateElement('div');
}
lynx.performance.profileStart.mockClear();
lynx.performance.profileEnd.mockClear();
lynx.performance.profileFlowId.mockClear();
- render(, scratch);
+ currentRender(currentCreateElement(App), scratch);
await Promise.resolve();
- render(null, scratch);
+ currentRender(null, scratch);
await Promise.resolve();
const starts = lynx.performance.profileStart.mock.calls;
@@ -103,10 +118,10 @@ describe('react hooks profile', () => {
function App() {
const [value, _setValue] = useState(0);
setValue = _setValue;
- return ;
+ return currentCreateElement('div', { value });
}
- render(, scratch);
+ currentRender(currentCreateElement(App), scratch);
const OriginalError = globalThis.Error;
class ErrorWithoutStack extends OriginalError {
@@ -139,7 +154,7 @@ describe('react hooks profile', () => {
function App() {
useEffect(() => undefined, []);
- return ;
+ return currentCreateElement('div');
}
const OriginalError = globalThis.Error;
@@ -155,7 +170,7 @@ describe('react hooks profile', () => {
try {
vi.stubGlobal('Error', ErrorWithoutStack);
- render(, scratch);
+ currentRender(currentCreateElement(App), scratch);
await Promise.resolve();
} finally {
vi.stubGlobal('Error', OriginalError);
@@ -179,10 +194,10 @@ describe('react hooks profile', () => {
function App() {
const [value, _setValue] = useState();
setValue = _setValue;
- return ;
+ return currentCreateElement('div', { value });
}
- render(, scratch);
+ currentRender(currentCreateElement(App), scratch);
lynx.performance.profileStart.mockClear();
lynx.performance.profileEnd.mockClear();
@@ -210,10 +225,10 @@ describe('react hooks profile', () => {
function App() {
const [value, _setValue] = useState(0);
setValue = _setValue;
- return ;
+ return currentCreateElement('div', { value });
}
- render(, scratch);
+ currentRender(currentCreateElement(App), scratch);
lynx.performance.profileStart.mockClear();
lynx.performance.profileEnd.mockClear();
@@ -245,7 +260,7 @@ describe('react hooks profile', () => {
setValue = _setValue;
useEffect(() => undefined, [value]);
useLayoutEffect(() => undefined, [value]);
- return ;
+ return currentCreateElement('div', { value });
}
lynx.performance.profileStart.mockClear();
@@ -256,7 +271,7 @@ describe('react hooks profile', () => {
expect(useEffect).toBe(preactHooks.useEffect);
expect(useLayoutEffect).toBe(preactHooks.useEffect);
- render(, scratch);
+ currentRender(currentCreateElement(App), scratch);
await Promise.resolve();
setValue(v => v + 1);
await Promise.resolve();
diff --git a/packages/react/runtime/__test__/snapshot/debug/vnodeSource.test.ts b/packages/react/runtime/__test__/snapshot/debug/vnodeSource.test.ts
index b76a663833..ec4b5cfb1d 100644
--- a/packages/react/runtime/__test__/snapshot/debug/vnodeSource.test.ts
+++ b/packages/react/runtime/__test__/snapshot/debug/vnodeSource.test.ts
@@ -12,7 +12,7 @@ import {
moveSnapshotVNodeSource,
setupVNodeSourceHook,
} from '../../../src/snapshot/debug/vnodeSource';
-import { DIFFED, DOM } from '../../../src/snapshot/renderToOpcodes/constants';
+import { DIFFED, DOM } from '../../../src/shared/render-constants';
describe('vnodeSource', () => {
let originalDiffed: typeof options[typeof DIFFED];
diff --git a/packages/react/runtime/__test__/snapshot/event.test.jsx b/packages/react/runtime/__test__/snapshot/event.test.jsx
index ad89d9089e..c14e3b9362 100644
--- a/packages/react/runtime/__test__/snapshot/event.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/event.test.jsx
@@ -12,7 +12,7 @@ import { snapshotPatchApply } from '../../src/snapshot/lifecycle/patch/snapshotP
import { injectUpdateMainThread } from '../../src/snapshot/lifecycle/patch/updateMainThread';
import { injectTt } from '../../src/snapshot/lynx/tt';
import { root } from '../../src/lynx-api';
-import { CHILDREN } from '../../src/snapshot/renderToOpcodes/constants';
+import { CHILDREN } from '../../src/shared/render-constants';
import { __root } from '../../src/root';
import { setupPage, backgroundSnapshotInstanceManager } from '../../src/snapshot';
import { globalEnvManager } from './utils/envManager';
diff --git a/packages/react/runtime/__test__/snapshot/lifecycle.test.jsx b/packages/react/runtime/__test__/snapshot/lifecycle.test.jsx
index 3859abf291..687a73ac99 100644
--- a/packages/react/runtime/__test__/snapshot/lifecycle.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/lifecycle.test.jsx
@@ -7,7 +7,7 @@ import { waitSchedule } from './utils/nativeMethod';
import { globalCommitTaskMap, replaceCommitHook } from '../../src/snapshot/lifecycle/patch/commit';
import { deinitGlobalSnapshotPatch, initGlobalSnapshotPatch } from '../../src/snapshot/lifecycle/patch/snapshotPatch';
import { LifecycleConstant } from '../../src/snapshot/lifecycle/constant';
-import { CATCH_ERROR } from '../../src/snapshot/renderToOpcodes/constants';
+import { CATCH_ERROR } from '../../src/shared/render-constants';
import { __root } from '../../src/root';
import { setupPage, backgroundSnapshotInstanceManager } from '../../src/snapshot';
diff --git a/packages/react/runtime/__test__/snapshot/lifecycle/destroy.test.jsx b/packages/react/runtime/__test__/snapshot/lifecycle/destroy.test.jsx
index 7e50173d07..78ae9116d7 100644
--- a/packages/react/runtime/__test__/snapshot/lifecycle/destroy.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/lifecycle/destroy.test.jsx
@@ -1,4 +1,3 @@
-import { render } from 'preact';
import { beforeAll, describe, expect, test, vi } from 'vitest';
describe('Destroy', () => {
@@ -21,6 +20,7 @@ describe('Destroy', () => {
expect(addEventListener).toHaveBeenCalled();
expect(removeEventListener).toHaveBeenCalledTimes(0);
+ const { h, render } = await import('preact');
const { useEffect } = await import('../../../src/index');
const { __root } = await import('../../../src/root');
@@ -33,7 +33,7 @@ describe('Destroy', () => {
return null;
}
- render(, __root);
+ render(h(Comp), __root);
await Promise.resolve().then(() => {});
expect(() => lynxCoreInject.tt.callDestroyLifetimeFun()).toThrow('???');
diff --git a/packages/react/runtime/__test__/snapshot/lifecycle/updateGlobalProps.event.test.jsx b/packages/react/runtime/__test__/snapshot/lifecycle/updateGlobalProps.event.test.jsx
new file mode 100644
index 0000000000..6a667e3fff
--- /dev/null
+++ b/packages/react/runtime/__test__/snapshot/lifecycle/updateGlobalProps.event.test.jsx
@@ -0,0 +1,234 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+
+import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
+
+import { __root } from '../../../src/root';
+import { replaceCommitHook } from '../../../src/snapshot/lifecycle/patch/commit';
+import { elementTree, waitSchedule } from '../utils/nativeMethod';
+import { globalEnvManager } from '../utils/envManager';
+
+beforeAll(() => {
+ replaceCommitHook();
+});
+
+beforeEach(() => {
+ globalEnvManager.resetEnv();
+ globalThis.__GLOBAL_PROPS_MODE__ = 'event';
+});
+
+afterEach(() => {
+ elementTree.clear();
+ vi.restoreAllMocks();
+ globalThis.__GLOBAL_PROPS_MODE__ = 'reactive';
+});
+
+async function renderWithCurrentPreact(element, root) {
+ const [preact, { document, setupBackgroundDocument }] = await Promise.all([
+ import('preact'),
+ import('../../../src/document'),
+ ]);
+ setupBackgroundDocument();
+ preact.options.document = document;
+ preact.render(element, root);
+}
+
+describe('updateGlobalProps event mode', () => {
+ it('should trigger re-render when useGlobalProps is called', async () => {
+ const { useGlobalProps, useGlobalPropsChanged } = await import('../../../src/lynx-api');
+
+ lynx.__globalProps = { theme: 'dark' };
+ let count = 0;
+ let dataTheme, globalPropsTheme;
+ const Comp = () => {
+ const globalProps = useGlobalProps();
+ useGlobalPropsChanged(data => {
+ count++;
+ dataTheme = data.theme;
+ globalPropsTheme = lynx.__globalProps.theme;
+ });
+ return {globalProps.theme};
+ };
+
+ // main thread render
+ {
+ __root.__jsx = ;
+ renderPage();
+ expect(count).toBe(0);
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+ `);
+ }
+
+ // background render
+ {
+ globalEnvManager.switchToBackground();
+ __root.__jsx = ;
+ await renderWithCurrentPreact(, __root);
+ }
+
+ // hydrate
+ {
+ lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
+ }
+
+ // rLynxChange
+ {
+ globalEnvManager.switchToMainThread();
+ globalThis.__OnLifecycleEvent.mockClear();
+ const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
+ globalThis[rLynxChange[0]](rLynxChange[1]);
+ await waitSchedule();
+ }
+
+ // updateGlobalProps
+ {
+ globalEnvManager.switchToBackground();
+ lynx.getNativeApp().callLepusMethod.mockClear();
+ lynxCoreInject.tt.updateGlobalProps({ theme: 'light' });
+ await waitSchedule();
+
+ // rLynxChange should be called
+ expect(lynx.getNativeApp().callLepusMethod.mock.calls.length).toBe(1);
+ expect(count).toBe(1);
+ expect(dataTheme).toBe('light');
+ expect(globalPropsTheme).toBe('light');
+ // ui change
+ globalEnvManager.switchToMainThread();
+ for (const rLynxChange of lynx.getNativeApp().callLepusMethod.mock.calls) {
+ globalThis[rLynxChange[0]](rLynxChange[1]);
+ rLynxChange[2]();
+ }
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+ `);
+ }
+ });
+
+ it('should trigger update when GlobalPropsProvider and useGlobalProps are used', async () => {
+ const { GlobalPropsProvider, GlobalPropsConsumer } = await import('../../../src/lynx-api');
+
+ lynx.__globalProps = { theme: 'dark' };
+ const Comp = () => {
+ return (
+
+
+ {globalProps => {
+ return {globalProps.theme};
+ }}
+
+
+ );
+ };
+
+ // main thread render
+ {
+ __root.__jsx = ;
+ renderPage();
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+ `);
+ }
+
+ // background render
+ {
+ globalEnvManager.switchToBackground();
+ __root.__jsx = ;
+ await renderWithCurrentPreact(, __root);
+ }
+
+ // hydrate
+ {
+ lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
+ }
+
+ // rLynxChange
+ {
+ globalEnvManager.switchToMainThread();
+ globalThis.__OnLifecycleEvent.mockClear();
+ const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
+ globalThis[rLynxChange[0]](rLynxChange[1]);
+ await waitSchedule();
+ }
+
+ // updateGlobalProps
+ {
+ globalEnvManager.switchToBackground();
+ lynx.getNativeApp().callLepusMethod.mockClear();
+ lynxCoreInject.tt.updateGlobalProps({ theme: 'light' });
+ await waitSchedule();
+
+ // rLynxChange should be called
+ expect(lynx.getNativeApp().callLepusMethod.mock.calls.length).toBe(2);
+ expect(lynx.getNativeApp().callLepusMethod.mock.calls).toMatchInlineSnapshot(`
+ [
+ [
+ "rLynxChange",
+ {
+ "data": "{"patchList":[{"id":6}]}",
+ "patchOptions": {
+ "flowIds": [
+ 666,
+ ],
+ "reloadVersion": 0,
+ },
+ },
+ [Function],
+ ],
+ [
+ "rLynxChange",
+ {
+ "data": "{"patchList":[{"id":7,"snapshotPatch":[3,-3,0,"light"]}]}",
+ "patchOptions": {
+ "reloadVersion": 0,
+ },
+ },
+ [Function],
+ ],
+ ]
+ `);
+ // ui change
+ globalEnvManager.switchToMainThread();
+ for (const rLynxChange of lynx.getNativeApp().callLepusMethod.mock.calls) {
+ globalThis[rLynxChange[0]](rLynxChange[1]);
+ rLynxChange[2]();
+ }
+ expect(__root.__element_root).toMatchInlineSnapshot(`
+
+
+
+
+
+ `);
+ }
+ });
+});
diff --git a/packages/react/runtime/__test__/snapshot/lifecycle/updateGlobalProps.test.jsx b/packages/react/runtime/__test__/snapshot/lifecycle/updateGlobalProps.test.jsx
index 380596045f..f61d9d4b38 100644
--- a/packages/react/runtime/__test__/snapshot/lifecycle/updateGlobalProps.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/lifecycle/updateGlobalProps.test.jsx
@@ -7,7 +7,6 @@ import { globalEnvManager } from '../utils/envManager';
import { describe } from 'vitest';
import { it } from 'vitest';
import { expect } from 'vitest';
-import { render } from 'preact';
import { waitSchedule } from '../utils/nativeMethod';
import { beforeAll } from 'vitest';
import { replaceCommitHook } from '../../../src/snapshot/lifecycle/patch/commit';
@@ -24,11 +23,20 @@ beforeEach(() => {
afterEach(() => {
elementTree.clear();
- vi.resetModules();
vi.restoreAllMocks();
globalThis.__GLOBAL_PROPS_MODE__ = 'reactive';
});
+async function renderWithCurrentPreact(element, root) {
+ const [preact, { document, setupBackgroundDocument }] = await Promise.all([
+ import('preact'),
+ import('../../../src/document'),
+ ]);
+ setupBackgroundDocument();
+ preact.options.document = document;
+ preact.render(element, root);
+}
+
describe('updateGlobalProps', () => {
it('should update global props', async () => {
lynx.__globalProps = { theme: 'dark' };
@@ -57,7 +65,7 @@ describe('updateGlobalProps', () => {
{
globalEnvManager.switchToBackground();
__root.__jsx = ;
- render(, __root);
+ await renderWithCurrentPreact(, __root);
}
// hydrate
@@ -141,7 +149,7 @@ describe('updateGlobalProps', () => {
{
globalEnvManager.switchToBackground();
__root.__jsx = ;
- render(, __root);
+ await renderWithCurrentPreact(, __root);
expect(console.warn).toBeCalledWith(expect.stringContaining('No need to use this API'));
}
@@ -240,7 +248,7 @@ describe('updateGlobalProps', () => {
{
globalEnvManager.switchToBackground();
__root.__jsx = ;
- render(, __root);
+ await renderWithCurrentPreact(, __root);
expect(console.warn).toBeCalledWith(expect.stringContaining('No need to use this API'));
}
@@ -329,7 +337,7 @@ describe('updateGlobalProps', () => {
{
globalEnvManager.switchToBackground();
__root.__jsx = ;
- render(, __root);
+ await renderWithCurrentPreact(, __root);
}
// hydrate
@@ -371,207 +379,4 @@ describe('updateGlobalProps', () => {
`);
}
});
-
- it('should trigger re-render when useGlobalProps is called', async () => {
- globalThis.__GLOBAL_PROPS_MODE__ = 'event';
- const { useGlobalProps, useGlobalPropsChanged, GlobalPropsProvider, GlobalPropsConsumer } = await import(
- '../../../src/lynx-api'
- );
-
- lynx.__globalProps = { theme: 'dark' };
- let count = 0;
- let dataTheme, globalPropsTheme;
- const Comp = () => {
- const globalProps = useGlobalProps();
- useGlobalPropsChanged(data => {
- count++;
- dataTheme = data.theme;
- globalPropsTheme = lynx.__globalProps.theme;
- });
- return {globalProps.theme};
- };
-
- // main thread render
- {
- __root.__jsx = ;
- renderPage();
- expect(count).toBe(0);
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
- `);
- }
-
- // background render
- {
- globalEnvManager.switchToBackground();
- __root.__jsx = ;
- render(, __root);
- }
-
- // hydrate
- {
- lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
- }
-
- // rLynxChange
- {
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- await waitSchedule();
- }
-
- // updateGlobalProps
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- lynxCoreInject.tt.updateGlobalProps({ theme: 'light' });
- await waitSchedule();
-
- // rLynxChange should be called
- expect(lynx.getNativeApp().callLepusMethod.mock.calls.length).toBe(1);
- expect(count).toBe(1);
- expect(dataTheme).toBe('light');
- expect(globalPropsTheme).toBe('light');
- // ui change
- globalEnvManager.switchToMainThread();
- for (const rLynxChange of lynx.getNativeApp().callLepusMethod.mock.calls) {
- globalThis[rLynxChange[0]](rLynxChange[1]);
- rLynxChange[2]();
- }
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
- `);
- }
- });
-
- it('should trigger update when GlobalPropsProvider and useGlobalProps are used', async () => {
- globalThis.__GLOBAL_PROPS_MODE__ = 'event';
- const { useGlobalProps, useGlobalPropsChanged, GlobalPropsProvider, GlobalPropsConsumer } = await import(
- '../../../src/lynx-api'
- );
-
- lynx.__globalProps = { theme: 'dark' };
- const Comp = () => {
- return (
-
-
- {globalProps => {
- return {globalProps.theme};
- }}
-
-
- );
- };
-
- // main thread render
- {
- __root.__jsx = ;
- renderPage();
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
- `);
- }
-
- // background render
- {
- globalEnvManager.switchToBackground();
- __root.__jsx = ;
- render(, __root);
- }
-
- // hydrate
- {
- lynxCoreInject.tt.OnLifecycleEvent(...globalThis.__OnLifecycleEvent.mock.calls[0]);
- }
-
- // rLynxChange
- {
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- await waitSchedule();
- }
-
- // updateGlobalProps
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- lynxCoreInject.tt.updateGlobalProps({ theme: 'light' });
- await waitSchedule();
-
- // rLynxChange should be called
- expect(lynx.getNativeApp().callLepusMethod.mock.calls.length).toBe(2);
- expect(lynx.getNativeApp().callLepusMethod.mock.calls).toMatchInlineSnapshot(`
- [
- [
- "rLynxChange",
- {
- "data": "{"patchList":[{"id":17}]}",
- "patchOptions": {
- "flowIds": [
- 666,
- ],
- "reloadVersion": 0,
- },
- },
- [Function],
- ],
- [
- "rLynxChange",
- {
- "data": "{"patchList":[{"id":18,"snapshotPatch":[3,-3,0,"light"]}]}",
- "patchOptions": {
- "reloadVersion": 0,
- },
- },
- [Function],
- ],
- ]
- `);
- // ui change
- globalEnvManager.switchToMainThread();
- for (const rLynxChange of lynx.getNativeApp().callLepusMethod.mock.calls) {
- globalThis[rLynxChange[0]](rLynxChange[1]);
- rLynxChange[2]();
- }
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
- `);
- }
- });
});
diff --git a/packages/react/runtime/src/core/hooks/mainThread.ts b/packages/react/runtime/src/core/hooks/mainThread.ts
new file mode 100644
index 0000000000..9609cbf4b6
--- /dev/null
+++ b/packages/react/runtime/src/core/hooks/mainThread.ts
@@ -0,0 +1,9 @@
+// Copyright 2026 The Lynx Authors. All rights reserved.
+// Licensed under the Apache License Version 2.0 that can be found in the
+// LICENSE file in the root directory of this source tree.
+
+import { installMainThreadHooks } from './mainThreadImpl.js';
+
+installMainThreadHooks();
+
+export * from './mainThreadImpl.js';
diff --git a/packages/react/runtime/src/snapshot/hooks/mainThread.ts b/packages/react/runtime/src/core/hooks/mainThreadImpl.ts
similarity index 77%
rename from packages/react/runtime/src/snapshot/hooks/mainThread.ts
rename to packages/react/runtime/src/core/hooks/mainThreadImpl.ts
index 1ed23bccbd..5973062d05 100644
--- a/packages/react/runtime/src/snapshot/hooks/mainThread.ts
+++ b/packages/react/runtime/src/core/hooks/mainThreadImpl.ts
@@ -3,7 +3,7 @@
// LICENSE file in the root directory of this source tree.
/**
- * Implements hooks in main thread.
+ * Implements hooks in the main thread.
* This module is modified from preact/hooks
*
* internal-preact/hooks/dist/hooks.mjs
@@ -19,7 +19,6 @@ import type {
useLayoutEffect as useLayoutEffectType,
} from 'preact/hooks';
-import { noop } from '../../utils.js';
import {
CHILDREN,
COMPONENT,
@@ -35,43 +34,55 @@ import {
ROOT,
VALUE,
VNODE,
-} from '../renderToOpcodes/constants.js';
+} from '../../shared/render-constants.js';
+import { noop } from '../../utils.js';
let currentIndex: number;
let currentComponent: Component | null | undefined;
let currentHook: number;
+let hooksInstalled = false;
+let oldBeforeDiff: ((vnode: any) => void) | undefined;
+let oldBeforeRender: ((vnode: any) => void) | undefined;
+let oldAfterDiff: ((vnode: any) => void) | undefined;
+let oldRoot: ((vnode: any, parentDom: any) => void) | undefined;
+
+function installMainThreadHooks(): void {
+ if (hooksInstalled) {
+ return;
+ }
+ hooksInstalled = true;
+ oldBeforeDiff = options[DIFF];
+ oldBeforeRender = options[RENDER];
+ oldAfterDiff = options[DIFFED];
+ oldRoot = options[ROOT];
+
+ options[DIFF] = function(vnode) {
+ currentComponent = null;
+ if (oldBeforeDiff) oldBeforeDiff(vnode);
+ };
-const oldBeforeDiff = options[DIFF];
-const oldBeforeRender = options[RENDER];
-const oldAfterDiff = options[DIFFED];
-const oldRoot = options[ROOT];
-
-options[DIFF] = function(vnode) {
- currentComponent = null;
- if (oldBeforeDiff) oldBeforeDiff(vnode);
-};
-
-/* v8 ignore start */
-options[ROOT] = function(vnode, parentDom) {
- // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- if (vnode && parentDom[CHILDREN] && parentDom[CHILDREN][MASK]) {
+ /* v8 ignore start */
+ options[ROOT] = function(vnode, parentDom) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
- vnode[MASK] = parentDom[CHILDREN][MASK] as [number, number];
- }
- if (oldRoot) oldRoot(vnode, parentDom);
-};
-/* v8 ignore stop */
+ if (vnode && parentDom[CHILDREN] && parentDom[CHILDREN][MASK]) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
+ vnode[MASK] = parentDom[CHILDREN][MASK] as [number, number];
+ }
+ if (oldRoot) oldRoot(vnode, parentDom);
+ };
+ /* v8 ignore stop */
-options[RENDER] = function(vnode) {
- if (oldBeforeRender) oldBeforeRender(vnode);
- currentComponent = vnode[COMPONENT];
- currentIndex = 0;
-};
+ options[RENDER] = function(vnode) {
+ if (oldBeforeRender) oldBeforeRender(vnode);
+ currentComponent = vnode[COMPONENT];
+ currentIndex = 0;
+ };
-options[DIFFED] = function(vnode) {
- if (oldAfterDiff) oldAfterDiff(vnode);
- currentComponent = null;
-};
+ options[DIFFED] = function(vnode) {
+ if (oldAfterDiff) oldAfterDiff(vnode);
+ currentComponent = null;
+ };
+}
function getHookState(index: number, type: number) {
if (options[HOOK]) {
@@ -197,6 +208,7 @@ const useLayoutEffect = noop as typeof useLayoutEffectType;
const useImperativeHandle = noop as typeof useImperativeHandleType;
export {
+ installMainThreadHooks,
useCallback,
useContext,
useDebugValue,
diff --git a/packages/react/runtime/src/snapshot/hooks/react.ts b/packages/react/runtime/src/core/hooks/react.ts
similarity index 99%
rename from packages/react/runtime/src/snapshot/hooks/react.ts
rename to packages/react/runtime/src/core/hooks/react.ts
index 3f96b43a36..b0994ca03e 100644
--- a/packages/react/runtime/src/snapshot/hooks/react.ts
+++ b/packages/react/runtime/src/core/hooks/react.ts
@@ -19,7 +19,7 @@ import type { DependencyList, EffectCallback } from 'react';
import type { TraceOption } from '@lynx-js/types';
-import { isProfiling, profileEnd, profileFlowId, profileStart } from '../debug/profile.js';
+import { isProfiling, profileEnd, profileFlowId, profileStart } from '../../shared/profile.js';
type GenericSetState = Dispatch>;
diff --git a/packages/react/runtime/src/snapshot/hooks/useLynxGlobalEventListener.ts b/packages/react/runtime/src/core/hooks/useLynxGlobalEventListener.ts
similarity index 95%
rename from packages/react/runtime/src/snapshot/hooks/useLynxGlobalEventListener.ts
rename to packages/react/runtime/src/core/hooks/useLynxGlobalEventListener.ts
index 8c180719ba..30d9ecf0e0 100644
--- a/packages/react/runtime/src/snapshot/hooks/useLynxGlobalEventListener.ts
+++ b/packages/react/runtime/src/core/hooks/useLynxGlobalEventListener.ts
@@ -4,7 +4,7 @@
import { useEffect, useMemo, useRef } from 'preact/hooks';
/**
- * `useLynxGlobalEventListener` help you `addListener` as early as possible.
+ * `useLynxGlobalEventListener` helps you `addListener` as early as possible.
*
* @example
*
diff --git a/packages/react/runtime/src/index.ts b/packages/react/runtime/src/index.ts
index a2be13406f..13f69eaf7f 100644
--- a/packages/react/runtime/src/index.ts
+++ b/packages/react/runtime/src/index.ts
@@ -31,12 +31,12 @@ import {
useReducer,
useRef,
useState,
-} from './snapshot/hooks/react.js';
+} from './core/hooks/react.js';
import { Suspense } from './snapshot/lynx/suspense.js';
export { Component, createContext } from 'preact';
export { PureComponent } from 'preact/compat';
-export * from './snapshot/hooks/react.js';
+export * from './core/hooks/react.js';
/**
* @internal
diff --git a/packages/react/runtime/src/internal.ts b/packages/react/runtime/src/internal.ts
index c5dfca947c..075d4af944 100644
--- a/packages/react/runtime/src/internal.ts
+++ b/packages/react/runtime/src/internal.ts
@@ -7,9 +7,9 @@ import type { FC } from 'react';
import './lynx.js';
+import { useMemo } from './core/hooks/react.js';
import { __root } from './root.js';
import { factory as factory2 } from './snapshot/compat/componentIs.js';
-import { useMemo } from './snapshot/hooks/react.js';
import { loadLazyBundle } from './snapshot/lynx/lazy-bundle.js';
import { BackgroundSnapshotInstance } from './snapshot/snapshot/backgroundSnapshot.js';
import { __page, __pageId, createSnapshot, snapshotManager } from './snapshot/snapshot/definition.js';
@@ -17,7 +17,7 @@ import { DynamicPartType } from './snapshot/snapshot/dynamicPartType.js';
import { snapshotCreateList } from './snapshot/snapshot/list.js';
import { SnapshotInstance, snapshotCreatorMap } from './snapshot/snapshot/snapshot.js';
-export { CHILDREN, COMPONENT, DIFF, DIRTY, DOM, FLAGS, INDEX, PARENT } from './snapshot/renderToOpcodes/constants.js';
+export { CHILDREN, COMPONENT, DIFF, DIRTY, DOM, FLAGS, INDEX, PARENT } from './shared/render-constants.js';
export { __page, __pageId, __root };
diff --git a/packages/react/runtime/src/lynx-api.ts b/packages/react/runtime/src/lynx-api.ts
index 180da40023..3d2cac947a 100644
--- a/packages/react/runtime/src/lynx-api.ts
+++ b/packages/react/runtime/src/lynx-api.ts
@@ -6,10 +6,10 @@ import { createContext, createElement } from 'preact/compat';
import { useState } from 'preact/hooks';
import type { Consumer, FC, ReactNode } from 'react';
+import { useLynxGlobalEventListener } from './core/hooks/useLynxGlobalEventListener.js';
import { __root } from './root.js';
+import { profileEnd, profileStart } from './shared/profile.js';
import { factory, withInitDataInState } from './snapshot/compat/initData.js';
-import { profileEnd, profileStart } from './snapshot/debug/profile.js';
-import { useLynxGlobalEventListener } from './snapshot/hooks/useLynxGlobalEventListener.js';
import { LifecycleConstant } from './snapshot/lifecycle/constant.js';
import { flushDelayedLifecycleEvents } from './snapshot/lynx/tt.js';
@@ -572,7 +572,7 @@ export interface Lynx {
registerDataProcessors: (dataProcessorDefinition?: DataProcessorDefinition) => void;
}
-export { useLynxGlobalEventListener } from './snapshot/hooks/useLynxGlobalEventListener.js';
+export { useLynxGlobalEventListener } from './core/hooks/useLynxGlobalEventListener.js';
export { runOnBackground } from './snapshot/worklet/call/runOnBackground.js';
export { runOnMainThread } from './snapshot/worklet/call/runOnMainThread.js';
export { MainThreadRef, useMainThreadRef } from './snapshot/worklet/ref/workletRef.js';
diff --git a/packages/react/runtime/src/lynx.ts b/packages/react/runtime/src/lynx.ts
index 2864d9c1be..e4a20519af 100644
--- a/packages/react/runtime/src/lynx.ts
+++ b/packages/react/runtime/src/lynx.ts
@@ -3,13 +3,13 @@
// LICENSE file in the root directory of this source tree.
import { options } from 'preact';
// to make sure preact's hooks to register earlier than ours
-import './snapshot/hooks/react.js';
+import './core/hooks/react.js';
import { document, setupBackgroundDocument } from './document.js';
+import { setupComponentStack } from './shared/component-stack.js';
+import { isProfiling } from './shared/profile.js';
import { initElementPAPICallAlog } from './snapshot/alog/elementPAPICall.js';
import { initAlog } from './snapshot/alog/index.js';
-import { setupComponentStack } from './snapshot/debug/component-stack.js';
-import { isProfiling } from './snapshot/debug/profile.js';
import { initProfileHook } from './snapshot/debug/profileHooks.js';
import { setupVNodeSourceHook } from './snapshot/debug/vnodeSource.js';
import { replaceCommitHook } from './snapshot/lifecycle/patch/commit.js';
diff --git a/packages/react/runtime/src/snapshot/debug/component-stack.ts b/packages/react/runtime/src/shared/component-stack.ts
similarity index 98%
rename from packages/react/runtime/src/snapshot/debug/component-stack.ts
rename to packages/react/runtime/src/shared/component-stack.ts
index a4474333b5..cab4eb1741 100644
--- a/packages/react/runtime/src/snapshot/debug/component-stack.ts
+++ b/packages/react/runtime/src/shared/component-stack.ts
@@ -30,7 +30,7 @@ SOFTWARE.
import { Fragment, options } from 'preact';
import type { VNode } from 'preact';
-import { DIFF, DIFFED, RENDER, ROOT } from '../renderToOpcodes/constants.js';
+import { DIFF, DIFFED, RENDER, ROOT } from './render-constants.js';
interface PatchedVNode extends VNode {
_owner?: PatchedVNode | null;
diff --git a/packages/react/runtime/src/snapshot/debug/profile.ts b/packages/react/runtime/src/shared/profile.ts
similarity index 82%
rename from packages/react/runtime/src/snapshot/debug/profile.ts
rename to packages/react/runtime/src/shared/profile.ts
index 1ae2fe6707..edb1c448a5 100644
--- a/packages/react/runtime/src/snapshot/debug/profile.ts
+++ b/packages/react/runtime/src/shared/profile.ts
@@ -2,14 +2,15 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
-import { noop } from '../../utils.js';
+/* v8 ignore start */
+const noop: (...args: unknown[]) => unknown = () => {};
+const noopFlowId = () => 0;
+/* v8 ignore end */
export const isProfiling: boolean = /* @__PURE__ */ Boolean(
lynx.performance?.isProfileRecording?.(),
);
-const noopFlowId = () => 0;
-
export const profileStart = /* @__PURE__ */ ((() => {
let p;
if (!(p = lynx.performance) || typeof p.profileStart !== 'function') {
@@ -26,10 +27,10 @@ export const profileEnd = /* @__PURE__ */ ((() => {
return p.profileEnd.bind(p);
})()) as typeof lynx.performance.profileEnd;
-export const profileFlowId = /* @__PURE__ */ ((() => {
+export const profileFlowId: typeof lynx.performance.profileFlowId = /* @__PURE__ */ (() => {
let p;
if (!(p = lynx.performance) || typeof p.profileFlowId !== 'function') {
return noopFlowId;
}
return p.profileFlowId.bind(p);
-})()) as typeof lynx.performance.profileFlowId;
+})();
diff --git a/packages/react/runtime/src/snapshot/renderToOpcodes/constants.ts b/packages/react/runtime/src/shared/render-constants.ts
similarity index 100%
rename from packages/react/runtime/src/snapshot/renderToOpcodes/constants.ts
rename to packages/react/runtime/src/shared/render-constants.ts
diff --git a/packages/react/runtime/src/snapshot/alog/elementPAPICall.ts b/packages/react/runtime/src/snapshot/alog/elementPAPICall.ts
index 0f7b78c263..9218a6c84d 100644
--- a/packages/react/runtime/src/snapshot/alog/elementPAPICall.ts
+++ b/packages/react/runtime/src/snapshot/alog/elementPAPICall.ts
@@ -2,7 +2,7 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
-import { profileEnd, profileStart } from '../debug/profile.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
const fiberElementPAPINameList = [
'__CreatePage',
diff --git a/packages/react/runtime/src/snapshot/alog/render.ts b/packages/react/runtime/src/snapshot/alog/render.ts
index a820261bd5..34b011a6db 100644
--- a/packages/react/runtime/src/snapshot/alog/render.ts
+++ b/packages/react/runtime/src/snapshot/alog/render.ts
@@ -4,8 +4,8 @@
import { options } from 'preact';
import type { ComponentClass, VNode } from 'preact';
+import { DIFFED, DOM } from '../../shared/render-constants.js';
import { getDisplayName } from '../../utils.js';
-import { DIFFED, DOM } from '../renderToOpcodes/constants.js';
import type { SnapshotInstance } from '../snapshot/snapshot.js';
export function initRenderAlog(): void {
diff --git a/packages/react/runtime/src/snapshot/compat/initData.ts b/packages/react/runtime/src/snapshot/compat/initData.ts
index fe2a6402d8..edeea7adf0 100644
--- a/packages/react/runtime/src/snapshot/compat/initData.ts
+++ b/packages/react/runtime/src/snapshot/compat/initData.ts
@@ -4,7 +4,7 @@
import type { ComponentChildren, Consumer, Context, Provider } from 'preact';
import type { ComponentClass } from 'react';
-import { useLynxGlobalEventListener } from '../hooks/useLynxGlobalEventListener.js';
+import { useLynxGlobalEventListener } from '../../core/hooks/useLynxGlobalEventListener.js';
import { globalFlushOptions } from '../lifecycle/patch/commit.js';
type Getter = {
@@ -16,7 +16,7 @@ export function factory(
{ createContext, useState, createElement, useLynxGlobalEventListener: useListener }: {
createContext: typeof import('preact').createContext;
useState: typeof import('preact/hooks').useState;
- createElement: typeof import('preact').createElement;
+ createElement: typeof import('preact/compat').createElement;
useLynxGlobalEventListener: typeof useLynxGlobalEventListener;
},
prop: '__globalProps' | '__initData',
diff --git a/packages/react/runtime/src/snapshot/debug/profileHooks.ts b/packages/react/runtime/src/snapshot/debug/profileHooks.ts
index d09c8c703b..884fac08c5 100644
--- a/packages/react/runtime/src/snapshot/debug/profileHooks.ts
+++ b/packages/react/runtime/src/snapshot/debug/profileHooks.ts
@@ -6,9 +6,6 @@ import type { ComponentClass, ComponentType, VNode } from 'preact';
import type { TraceOption } from '@lynx-js/types';
-import { getDisplayName, hook } from '../../utils.js';
-import { globalPatchOptions } from '../lifecycle/patch/commit.js';
-import { __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js';
import {
COMMIT,
COMPONENT,
@@ -23,7 +20,10 @@ import {
RENDER,
VALUE,
VNODE,
-} from '../renderToOpcodes/constants.js';
+} from '../../shared/render-constants.js';
+import { getDisplayName, hook } from '../../utils.js';
+import { globalPatchOptions } from '../lifecycle/patch/commit.js';
+import { __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js';
const format = (val: unknown) => {
if (typeof val === 'function') {
diff --git a/packages/react/runtime/src/snapshot/debug/vnodeSource.ts b/packages/react/runtime/src/snapshot/debug/vnodeSource.ts
index aa8e9f8843..d1231573a2 100644
--- a/packages/react/runtime/src/snapshot/debug/vnodeSource.ts
+++ b/packages/react/runtime/src/snapshot/debug/vnodeSource.ts
@@ -4,7 +4,7 @@
import { options } from 'preact';
import type { VNode } from 'preact';
-import { DIFFED, DOM } from '../renderToOpcodes/constants.js';
+import { DIFFED, DOM } from '../../shared/render-constants.js';
interface SourceInfo {
fileName?: string;
diff --git a/packages/react/runtime/src/snapshot/legacy-react-runtime/index.ts b/packages/react/runtime/src/snapshot/legacy-react-runtime/index.ts
index ff74990cca..716732542b 100644
--- a/packages/react/runtime/src/snapshot/legacy-react-runtime/index.ts
+++ b/packages/react/runtime/src/snapshot/legacy-react-runtime/index.ts
@@ -4,11 +4,11 @@
import { createContext, createRef } from 'preact';
import { lazy } from 'preact/compat';
+import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from '../../core/hooks/react.js';
import {
ComponentFromReactRuntime as Component,
ComponentFromReactRuntime as PureComponent,
} from '../compat/lynxComponent.js';
-import { useCallback, useEffect, useMemo, useReducer, useRef, useState } from '../hooks/react.js';
/* v8 ignore next 3 */
function __runInJS(value: T): T | undefined | null {
@@ -20,7 +20,7 @@ export { ComponentFromReactRuntime as Component } from '../compat/lynxComponent.
export { ComponentFromReactRuntime as PureComponent } from '../compat/lynxComponent.js';
export { createContext } from 'preact';
export { lazy } from 'preact/compat';
-export { useState, useReducer, useEffect, useMemo, useCallback /*, useInstance */ } from '../hooks/react.js';
+export { useState, useReducer, useEffect, useMemo, useCallback /*, useInstance */ } from '../../core/hooks/react.js';
export { __runInJS, createRef, useRef };
/**
diff --git a/packages/react/runtime/src/snapshot/lifecycle/destroy.ts b/packages/react/runtime/src/snapshot/lifecycle/destroy.ts
index ce65c38d8a..4700517020 100644
--- a/packages/react/runtime/src/snapshot/lifecycle/destroy.ts
+++ b/packages/react/runtime/src/snapshot/lifecycle/destroy.ts
@@ -7,7 +7,7 @@ import { __root } from '../../root.js';
import { delayedEvents } from './event/delayEvents.js';
import { delayedLifecycleEvents } from './event/delayLifecycleEvents.js';
import { globalCommitTaskMap } from './patch/commit.js';
-import { profileEnd, profileStart } from '../debug/profile.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
function destroyBackground(): void {
if (typeof __PROFILE__ !== 'undefined' && __PROFILE__) {
diff --git a/packages/react/runtime/src/snapshot/lifecycle/event/jsReady.ts b/packages/react/runtime/src/snapshot/lifecycle/event/jsReady.ts
index 4fcda32269..6e2d489061 100644
--- a/packages/react/runtime/src/snapshot/lifecycle/event/jsReady.ts
+++ b/packages/react/runtime/src/snapshot/lifecycle/event/jsReady.ts
@@ -2,7 +2,7 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { __root } from '../../../root.js';
-import { profileEnd, profileStart } from '../../debug/profile.js';
+import { profileEnd, profileStart } from '../../../shared/profile.js';
import { LifecycleConstant } from '../../lifecycle/constant.js';
let isJSReady: boolean;
diff --git a/packages/react/runtime/src/snapshot/lifecycle/isRendering.ts b/packages/react/runtime/src/snapshot/lifecycle/isRendering.ts
index ac0d933c9f..3be5c3e1ea 100644
--- a/packages/react/runtime/src/snapshot/lifecycle/isRendering.ts
+++ b/packages/react/runtime/src/snapshot/lifecycle/isRendering.ts
@@ -4,8 +4,8 @@
import { options } from 'preact';
+import { RENDER_COMPONENT, ROOT } from '../../shared/render-constants.js';
import { hook, lynxQueueMicrotask } from '../../utils.js';
-import { RENDER_COMPONENT, ROOT } from '../renderToOpcodes/constants.js';
export const isRendering = /* @__PURE__ */ { value: false };
diff --git a/packages/react/runtime/src/snapshot/lifecycle/patch/commit.ts b/packages/react/runtime/src/snapshot/lifecycle/patch/commit.ts
index 531f108dfb..115336fb31 100644
--- a/packages/react/runtime/src/snapshot/lifecycle/patch/commit.ts
+++ b/packages/react/runtime/src/snapshot/lifecycle/patch/commit.ts
@@ -29,11 +29,11 @@ import {
} from './globalState.js';
import { takeGlobalSnapshotPatch } from './snapshotPatch.js';
import type { SnapshotPatch } from './snapshotPatch.js';
+import { profileEnd, profileStart } from '../../../shared/profile.js';
+import { COMMIT } from '../../../shared/render-constants.js';
import { hook, isEmptyObject } from '../../../utils.js';
-import { profileEnd, profileStart } from '../../debug/profile.js';
import { LifecycleConstant } from '../../lifecycle/constant.js';
import { globalPipelineOptions, markTiming, markTimingLegacy, setPipeline } from '../../lynx/performance.js';
-import { COMMIT } from '../../renderToOpcodes/constants.js';
import { backgroundSnapshotInstanceManager } from '../../snapshot/backgroundSnapshot.js';
import { applyQueuedRefs } from '../../snapshot/ref.js';
import {
diff --git a/packages/react/runtime/src/snapshot/lifecycle/reload.ts b/packages/react/runtime/src/snapshot/lifecycle/reload.ts
index 88ec0692d9..5d8b1bbafe 100644
--- a/packages/react/runtime/src/snapshot/lifecycle/reload.ts
+++ b/packages/react/runtime/src/snapshot/lifecycle/reload.ts
@@ -13,8 +13,8 @@ import { destroyBackground } from './destroy.js';
import { increaseReloadVersion } from './pass.js';
import { renderMainThread } from './render.js';
import { __root, setRoot } from '../../root.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
import { isEmptyObject } from '../../utils.js';
-import { profileEnd, profileStart } from '../debug/profile.js';
import { LifecycleConstant } from '../lifecycle/constant.js';
import { __pendingListUpdates } from '../list/pendingListUpdates.js';
import { hydrate } from '../renderToOpcodes/hydrate.js';
diff --git a/packages/react/runtime/src/snapshot/lifecycle/render.ts b/packages/react/runtime/src/snapshot/lifecycle/render.ts
index 04d7b541c4..f14b6246c6 100644
--- a/packages/react/runtime/src/snapshot/lifecycle/render.ts
+++ b/packages/react/runtime/src/snapshot/lifecycle/render.ts
@@ -7,7 +7,7 @@
*/
import { __root } from '../../root.js';
-import { profileEnd, profileStart } from '../debug/profile.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
import { render as renderToString } from '../renderToOpcodes/index.js';
import { SnapshotInstance } from '../snapshot/snapshot.js';
diff --git a/packages/react/runtime/src/snapshot/list/listUpdateInfo.ts b/packages/react/runtime/src/snapshot/list/listUpdateInfo.ts
index c53bc69bd4..87291908e8 100644
--- a/packages/react/runtime/src/snapshot/list/listUpdateInfo.ts
+++ b/packages/react/runtime/src/snapshot/list/listUpdateInfo.ts
@@ -2,7 +2,7 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { componentAtIndexFactory, enqueueComponentFactory } from './list.js';
-import { profileEnd, profileStart } from '../debug/profile.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
import { hydrate } from '../renderToOpcodes/hydrate.js';
import type { SnapshotInstance } from '../snapshot/snapshot.js';
diff --git a/packages/react/runtime/src/snapshot/lynx/component.ts b/packages/react/runtime/src/snapshot/lynx/component.ts
index c8021fecd5..c2fc207473 100644
--- a/packages/react/runtime/src/snapshot/lynx/component.ts
+++ b/packages/react/runtime/src/snapshot/lynx/component.ts
@@ -8,7 +8,7 @@ import { Component } from 'preact';
import { PerfSpecificKey, markTimingLegacy } from './performance.js';
import { globalFlushOptions } from '../lifecycle/patch/commit.js';
-import { NEXT_STATE } from '../renderToOpcodes/constants.js';
+import { NEXT_STATE } from '../../shared/render-constants.js';
if (__JS__) {
function reportRefDeprecationError(fnName: string, newFnName: string) {
diff --git a/packages/react/runtime/src/snapshot/lynx/env.ts b/packages/react/runtime/src/snapshot/lynx/env.ts
index 5fa934977d..d8ced4d26e 100644
--- a/packages/react/runtime/src/snapshot/lynx/env.ts
+++ b/packages/react/runtime/src/snapshot/lynx/env.ts
@@ -2,7 +2,7 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import type { DataProcessorDefinition, InitData, InitDataRaw } from '../../lynx-api.js';
-import { profileEnd, profileStart } from '../debug/profile.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
export function setupLynxEnv(): void {
if (!__LEPUS__) {
diff --git a/packages/react/runtime/src/snapshot/lynx/performance.ts b/packages/react/runtime/src/snapshot/lynx/performance.ts
index f12d6bf8a3..864f3b7d95 100644
--- a/packages/react/runtime/src/snapshot/lynx/performance.ts
+++ b/packages/react/runtime/src/snapshot/lynx/performance.ts
@@ -3,9 +3,9 @@
// LICENSE file in the root directory of this source tree.
import { options } from 'preact';
+import { RENDER_COMPONENT, ROOT } from '../../shared/render-constants.js';
import { hook, isSdkVersionGt } from '../../utils.js';
import { __globalSnapshotPatch } from '../lifecycle/patch/snapshotPatch.js';
-import { RENDER_COMPONENT, ROOT } from '../renderToOpcodes/constants.js';
const PerformanceTimingKeys = [
'updateSetStateTrigger',
diff --git a/packages/react/runtime/src/snapshot/lynx/runWithForce.ts b/packages/react/runtime/src/snapshot/lynx/runWithForce.ts
index 236ed3361c..3b21761449 100644
--- a/packages/react/runtime/src/snapshot/lynx/runWithForce.ts
+++ b/packages/react/runtime/src/snapshot/lynx/runWithForce.ts
@@ -5,7 +5,7 @@ import { options } from 'preact';
import type { VNode } from 'preact';
import { __root } from '../../root.js';
-import { COMPONENT, DIFF2, FORCE, ORIGINAL } from '../renderToOpcodes/constants.js';
+import { COMPONENT, DIFF2, FORCE, ORIGINAL } from '../../shared/render-constants.js';
export function runWithForce(cb: () => void): void {
// In https://github.com/preactjs/preact/pull/4724, preact will
diff --git a/packages/react/runtime/src/snapshot/lynx/tt.ts b/packages/react/runtime/src/snapshot/lynx/tt.ts
index 5e31140e65..83f141a44a 100644
--- a/packages/react/runtime/src/snapshot/lynx/tt.ts
+++ b/packages/react/runtime/src/snapshot/lynx/tt.ts
@@ -6,8 +6,9 @@ import { process, render } from 'preact';
import { PerformanceTimingFlags, PipelineOrigins, beginPipeline, markTiming } from './performance.js';
import { runWithForce } from './runWithForce.js';
import { __root } from '../../root.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
+import { CHILDREN } from '../../shared/render-constants.js';
import { printSnapshotInstanceToString } from '../debug/printSnapshot.js';
-import { profileEnd, profileStart } from '../debug/profile.js';
import { getSnapshotVNodeSource } from '../debug/vnodeSource.js';
import { LifecycleConstant, NativeUpdateDataType } from '../lifecycle/constant.js';
import type { FirstScreenData } from '../lifecycle/constant.js';
@@ -19,7 +20,6 @@ import type { PatchList } from '../lifecycle/patch/commit.js';
import { removeCtxNotFoundEventListener } from '../lifecycle/patch/error.js';
import { runDelayedUiOps } from '../lifecycle/ref/delay.js';
import { reloadBackground } from '../lifecycle/reload.js';
-import { CHILDREN } from '../renderToOpcodes/constants.js';
import {
BackgroundSnapshotInstance,
backgroundSnapshotInstanceManager,
diff --git a/packages/react/runtime/src/snapshot/renderToOpcodes/hydrate.ts b/packages/react/runtime/src/snapshot/renderToOpcodes/hydrate.ts
index 40987fd216..5f2b60af85 100644
--- a/packages/react/runtime/src/snapshot/renderToOpcodes/hydrate.ts
+++ b/packages/react/runtime/src/snapshot/renderToOpcodes/hydrate.ts
@@ -2,7 +2,7 @@
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
-import { profileEnd, profileStart } from '../debug/profile.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from '../list/list.js';
import { __pendingListUpdates } from '../list/pendingListUpdates.js';
import { DynamicPartType } from '../snapshot/dynamicPartType.js';
diff --git a/packages/react/runtime/src/snapshot/renderToOpcodes/index.ts b/packages/react/runtime/src/snapshot/renderToOpcodes/index.ts
index 451eb450bb..d44217adba 100644
--- a/packages/react/runtime/src/snapshot/renderToOpcodes/index.ts
+++ b/packages/react/runtime/src/snapshot/renderToOpcodes/index.ts
@@ -28,7 +28,7 @@ import {
VNODE,
HOOK,
CHILD_DID_SUSPEND,
-} from './constants.js';
+} from '../../shared/render-constants.js';
/** @typedef {import('preact').VNode} VNode */
diff --git a/packages/react/runtime/src/snapshot/renderToOpcodes/opcodes.ts b/packages/react/runtime/src/snapshot/renderToOpcodes/opcodes.ts
index 2428922b49..3ae2425950 100644
--- a/packages/react/runtime/src/snapshot/renderToOpcodes/opcodes.ts
+++ b/packages/react/runtime/src/snapshot/renderToOpcodes/opcodes.ts
@@ -3,7 +3,7 @@
// LICENSE file in the root directory of this source tree.
import { hydrate } from './hydrate.js';
import { componentAtIndexFactory, enqueueComponentFactory, gRecycleMap, gSignMap } from '../list/list.js';
-import { CHILDREN } from './constants.js';
+import { CHILDREN } from '../../shared/render-constants.js';
import { SnapshotInstance } from '../snapshot/snapshot.js';
const OpcodeBegin = 0;
diff --git a/packages/react/runtime/src/snapshot/snapshot/backgroundSnapshot.ts b/packages/react/runtime/src/snapshot/snapshot/backgroundSnapshot.ts
index cef524af06..55991a6331 100644
--- a/packages/react/runtime/src/snapshot/snapshot/backgroundSnapshot.ts
+++ b/packages/react/runtime/src/snapshot/snapshot/backgroundSnapshot.ts
@@ -20,8 +20,8 @@ import { hydrationMap } from './snapshotInstanceHydrationMap.js';
import { transformSpread } from './spread.js';
import type { SerializedSnapshotInstance } from './types.js';
import { traverseSnapshotInstance } from './utils.js';
+import { profileEnd, profileStart } from '../../shared/profile.js';
import { isDirectOrDeepEqual } from '../../utils.js';
-import { profileEnd, profileStart } from '../debug/profile.js';
import { clearSnapshotVNodeSource, getSnapshotVNodeSource, moveSnapshotVNodeSource } from '../debug/vnodeSource.js';
import { prepareGestureForCommit } from '../gesture/processGestureBagkround.js';
import type { GestureKind } from '../gesture/types.js';
diff --git a/packages/react/runtime/src/snapshot/worklet/ref/workletRef.ts b/packages/react/runtime/src/snapshot/worklet/ref/workletRef.ts
index 427f45b051..5ab4b7c82f 100644
--- a/packages/react/runtime/src/snapshot/worklet/ref/workletRef.ts
+++ b/packages/react/runtime/src/snapshot/worklet/ref/workletRef.ts
@@ -7,7 +7,7 @@ import type { WorkletRefImpl } from '@lynx-js/react/worklet-runtime/bindings';
import { WorkletEvents } from '@lynx-js/react/worklet-runtime/bindings';
import { addWorkletRefInitValue } from './workletRefPool.js';
-import { useMemo } from '../../hooks/react.js';
+import { useMemo } from '../../../core/hooks/react.js';
// Split into two variables for testing purposes
let lastIdBG = 0;
diff --git a/packages/react/runtime/src/utils.ts b/packages/react/runtime/src/utils.ts
index c5c4d562cd..8815acb55c 100644
--- a/packages/react/runtime/src/utils.ts
+++ b/packages/react/runtime/src/utils.ts
@@ -4,7 +4,7 @@
import type { ComponentClass } from 'preact';
-import { getCurrentVNode, getOwnerStack } from './snapshot/debug/component-stack.js';
+import { getCurrentVNode, getOwnerStack } from './shared/component-stack.js';
/* v8 ignore start */
export const noop: (...args: unknown[]) => unknown = () => {};
diff --git a/packages/react/runtime/vitest.config.ts b/packages/react/runtime/vitest.config.ts
index 69747ac0e3..c38141f08a 100644
--- a/packages/react/runtime/vitest.config.ts
+++ b/packages/react/runtime/vitest.config.ts
@@ -6,6 +6,7 @@ import { defineConfig } from 'vitest/config';
const require = createRequire(import.meta.url);
const runtimePkg = require.resolve('./src/internal.ts');
+const internalPreactRoot = path.dirname(require.resolve('preact/package.json'));
function transformReactLynxPlugin(): Plugin {
return {
@@ -60,20 +61,42 @@ export default defineConfig({
transformReactLynxPlugin(),
],
resolve: {
- alias: {
- '@lynx-js/react/compat': path.resolve(__dirname, './compat/index.js'),
- '@lynx-js/react/worklet-runtime/bindings': path.resolve(__dirname, './src/worklet-runtime/bindings/index.ts'),
- '@lynx-js/react/runtime-components': path.resolve(__dirname, '../components/src/index.ts'),
- '@lynx-js/react/internal': path.resolve(__dirname, './src/internal.ts'),
- '@lynx-js/react/jsx-dev-runtime': path.resolve(__dirname, './jsx-dev-runtime/index.js'),
- '@lynx-js/react/jsx-runtime': path.resolve(__dirname, './jsx-runtime/index.js'),
- '@lynx-js/react/lepus': path.resolve(__dirname, './lepus/index.js'),
- '@lynx-js/react/legacy-react-runtime': path.resolve(__dirname, './src/snapshot/legacy-react-runtime/index.ts'),
- '@lynx-js/react': path.resolve(__dirname, './src/index.ts'),
- },
+ dedupe: ['preact'],
+ alias: [
+ { find: /^preact$/, replacement: path.join(internalPreactRoot, 'dist/preact.mjs') },
+ { find: /^preact\/compat$/, replacement: path.join(internalPreactRoot, 'compat/dist/compat.mjs') },
+ { find: /^preact\/hooks$/, replacement: path.join(internalPreactRoot, 'hooks/dist/hooks.mjs') },
+ {
+ find: /^preact\/jsx-dev-runtime$/,
+ replacement: path.join(internalPreactRoot, 'jsx-runtime/dist/jsxRuntime.mjs'),
+ },
+ { find: /^preact\/jsx-runtime$/, replacement: path.join(internalPreactRoot, 'jsx-runtime/dist/jsxRuntime.mjs') },
+ { find: '@lynx-js/react/compat', replacement: path.resolve(__dirname, './compat/index.js') },
+ {
+ find: '@lynx-js/react/worklet-runtime/bindings',
+ replacement: path.resolve(__dirname, './src/worklet-runtime/bindings/index.ts'),
+ },
+ { find: '@lynx-js/react/runtime-components', replacement: path.resolve(__dirname, '../components/src/index.ts') },
+ { find: '@lynx-js/react/internal', replacement: path.resolve(__dirname, './src/internal.ts') },
+ { find: '@lynx-js/react/jsx-dev-runtime', replacement: path.resolve(__dirname, './jsx-dev-runtime/index.js') },
+ { find: '@lynx-js/react/jsx-runtime', replacement: path.resolve(__dirname, './jsx-runtime/index.js') },
+ { find: '@lynx-js/react/lepus', replacement: path.resolve(__dirname, './lepus/index.js') },
+ {
+ find: '@lynx-js/react/legacy-react-runtime',
+ replacement: path.resolve(__dirname, './src/snapshot/legacy-react-runtime/index.ts'),
+ },
+ { find: '@lynx-js/react', replacement: path.resolve(__dirname, './src/index.ts') },
+ ],
},
test: {
name: 'react/runtime',
+ server: {
+ deps: {
+ inline: [
+ /@lynx-js\/internal-preact/,
+ ],
+ },
+ },
coverage: {
exclude: [
'debug',
@@ -85,7 +108,10 @@ export default defineConfig({
'__test__/snapshot/utils/**',
'lib/**',
'worklet-runtime/**',
+ 'src/shared/component-stack.ts',
+ 'src/shared/profile.ts',
'src/index.ts',
+ 'src/lynx-api.ts',
'src/lynx.ts',
'src/root.ts',
'src/worklet-runtime/api/lepusQuerySelector.ts',
@@ -95,8 +121,8 @@ export default defineConfig({
'src/worklet-runtime/index.ts',
'src/worklet-runtime/listeners.ts',
'src/worklet-runtime/types/**',
- 'src/snapshot/debug/component-stack.ts',
'src/snapshot/debug/debug.ts',
+ 'src/snapshot/debug/profileHooks.ts',
'src/snapshot/debug/utils.ts',
'src/snapshot/lynx/calledByNative.ts',
'src/snapshot/lynx/component.ts',
@@ -104,6 +130,8 @@ export default defineConfig({
'src/snapshot/lynx/env.ts',
'src/snapshot/lynx/tt.ts',
'src/snapshot/compat/componentIs.ts',
+ 'src/snapshot/snapshot/types.ts',
+ 'src/snapshot/worklet/hmr.ts',
'__test__/snapshot/page.test.jsx',
'**/*.d.ts',
diff --git a/packages/react/types/react.docs.d.ts b/packages/react/types/react.docs.d.ts
index cae483da0c..a6a02b5a33 100644
--- a/packages/react/types/react.docs.d.ts
+++ b/packages/react/types/react.docs.d.ts
@@ -48,7 +48,7 @@ export {
useSyncExternalStore,
} from 'react';
-export { useEffect, useLayoutEffect, useErrorBoundary } from '../runtime/lib/snapshot/hooks/react.js';
+export { useEffect, useLayoutEffect, useErrorBoundary } from '../runtime/lib/core/hooks/react.js';
/**
* Built-in React APIs
diff --git a/packages/rspeedy/plugin-react-alias/test/index.test.ts b/packages/rspeedy/plugin-react-alias/test/index.test.ts
index d0dd151d45..83f7316232 100644
--- a/packages/rspeedy/plugin-react-alias/test/index.test.ts
+++ b/packages/rspeedy/plugin-react-alias/test/index.test.ts
@@ -212,7 +212,7 @@ describe('React - alias', () => {
expect(mainThreadRule.resolve.alias).toHaveProperty(
'preact/hooks',
expect.stringContaining(
- '/packages/react/runtime/lib/snapshot/hooks/mainThread.js'.replaceAll(
+ '/packages/react/runtime/lib/core/hooks/mainThread.js'.replaceAll(
'/',
path.sep,
),
@@ -221,7 +221,7 @@ describe('React - alias', () => {
expect(mainThreadRule.resolve.alias).toHaveProperty(
'@lynx-js/react/hooks',
expect.stringContaining(
- '/packages/react/runtime/lib/snapshot/hooks/mainThread.js'.replaceAll(
+ '/packages/react/runtime/lib/core/hooks/mainThread.js'.replaceAll(
'/',
path.sep,
),
@@ -230,7 +230,7 @@ describe('React - alias', () => {
expect(mainThreadRule.resolve.alias).toHaveProperty(
'@lynx-js/react/lepus/hooks',
expect.stringContaining(
- '/packages/react/runtime/lib/snapshot/hooks/mainThread.js'.replaceAll(
+ '/packages/react/runtime/lib/core/hooks/mainThread.js'.replaceAll(
'/',
path.sep,
),
@@ -272,7 +272,7 @@ describe('React - alias', () => {
expect(backgroundRule.resolve.alias).toHaveProperty(
'@lynx-js/react/hooks',
expect.stringContaining(
- '/packages/react/runtime/lib/snapshot/hooks/react.js'.replaceAll(
+ '/packages/react/runtime/lib/core/hooks/react.js'.replaceAll(
'/',
path.sep,
),