diff --git a/.changeset/clear-cobras-work.md b/.changeset/clear-cobras-work.md
new file mode 100644
index 0000000000..bfcf1f119a
--- /dev/null
+++ b/.changeset/clear-cobras-work.md
@@ -0,0 +1,22 @@
+---
+"@lynx-js/web-mainthread-apis": patch
+"@lynx-js/web-core": patch
+---
+
+fix: some inline style properties cause crash
+
+add support for the following css properties
+
+- mask
+- mask-repeat
+- mask-position
+- mask-clip
+- mask-origin
+- mask-size
+- gap
+- column-gap
+- row-gap
+- image-rendering
+- hyphens
+- offset-path
+- offset-distance
diff --git a/.changeset/easy-regions-say.md b/.changeset/easy-regions-say.md
new file mode 100644
index 0000000000..0d5e0fca74
--- /dev/null
+++ b/.changeset/easy-regions-say.md
@@ -0,0 +1,5 @@
+---
+"@lynx-js/web-elements": patch
+---
+
+fix(web): x-swiper-item threshold updated to 20
diff --git a/.changeset/five-frogs-report.md b/.changeset/five-frogs-report.md
new file mode 100644
index 0000000000..8028b00c3f
--- /dev/null
+++ b/.changeset/five-frogs-report.md
@@ -0,0 +1,5 @@
+---
+"@lynx-js/web-elements": patch
+---
+
+fix: In React19, setter and getter functions are treated as properties, making it impossible to retrieve the current value via attributes.
diff --git a/.changeset/modern-peaches-marry.md b/.changeset/modern-peaches-marry.md
new file mode 100644
index 0000000000..1994b45796
--- /dev/null
+++ b/.changeset/modern-peaches-marry.md
@@ -0,0 +1,5 @@
+---
+"@lynx-js/web-platform-rsbuild-plugin": minor
+---
+
+feat: Provides Rsbuild plugin for Web projects in Lynx Web Platform, currently supports polyfill about lynx.
diff --git a/.changeset/silly-rocks-guess.md b/.changeset/silly-rocks-guess.md
new file mode 100644
index 0000000000..d1845455b0
--- /dev/null
+++ b/.changeset/silly-rocks-guess.md
@@ -0,0 +1,7 @@
+---
+"@lynx-js/react": minor
+---
+
+Reverts #239: "batch multiple patches for main thread communication"
+
+This reverts the change that batched updates sent to the main thread in a single render pass.
diff --git a/.changeset/strong-rockets-itch.md b/.changeset/strong-rockets-itch.md
new file mode 100644
index 0000000000..7d675e7aba
--- /dev/null
+++ b/.changeset/strong-rockets-itch.md
@@ -0,0 +1,6 @@
+---
+"@lynx-js/react-rsbuild-plugin": patch
+"@lynx-js/react-webpack-plugin": patch
+---
+
+Support `@lynx-js/react` v0.108.0.
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 78f7a0f6b5..e3dc9d2c12 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -236,6 +236,27 @@ jobs:
export ALL_ON_UI=true
pnpm --filter @lynx-js/web-tests run test --reporter='github,dot,junit,html'
pnpm --filter @lynx-js/web-tests run coverage:ci
+ test-web-platform:
+ needs: build
+ uses: ./.github/workflows/workflow-test.yml
+ secrets:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+ with:
+ runs-on: lynx-ubuntu-24.04-medium
+ run: >
+ pnpm
+ --filter @lynx-js/web-platform-rsbuild-plugin
+ run test
+ --reporter=github-actions
+ --reporter=dot
+ --reporter=junit
+ --outputFile=test-report.junit.xml
+ --coverage.reporter='json'
+ --coverage.reporter='text'
+ --test-timeout=50000
+ --no-cache
+ --logHeapUsage
+ --silent
lighthouse:
needs: build
uses: ./.github/workflows/workflow-test.yml
@@ -286,6 +307,7 @@ jobs:
- code-style-check
- playwright-linux
- playwright-linux-all-on-ui
+ - test-web-platform
- test-plugins
- test-publish
- test-react
diff --git a/.gitignore b/.gitignore
index 6b2ffb578a..4056f6442c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,4 +45,7 @@ target/
.pnpm-store
# lighthouse-ci
-.lighthouseci
\ No newline at end of file
+.lighthouseci
+
+# rslib
+.rslib
diff --git a/package.json b/package.json
index 5b586f4dd3..e0a619088b 100644
--- a/package.json
+++ b/package.json
@@ -47,7 +47,7 @@
"typescript-eslint": "^8.31.0",
"vitest": "^3.1.2"
},
- "packageManager": "pnpm@10.9.0",
+ "packageManager": "pnpm@10.10.0",
"engines": {
"node": ">=22"
}
diff --git a/packages/react/runtime/__test__/lifecycle.test.jsx b/packages/react/runtime/__test__/lifecycle.test.jsx
index 95087ac645..bfb64ad1e5 100644
--- a/packages/react/runtime/__test__/lifecycle.test.jsx
+++ b/packages/react/runtime/__test__/lifecycle.test.jsx
@@ -1,20 +1,18 @@
-import { Component, options } from 'preact';
+import { Component, options, render } from 'preact';
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { useEffect, useLayoutEffect, useState } from '../src/index';
import { globalEnvManager } from './utils/envManager';
+import { waitSchedule } from './utils/nativeMethod';
import { initDelayUnmount } from '../src/lifecycle/delayUnmount';
import { globalCommitTaskMap, replaceCommitHook, replaceRequestAnimationFrame } from '../src/lifecycle/patch/commit';
import { deinitGlobalSnapshotPatch, initGlobalSnapshotPatch } from '../src/lifecycle/patch/snapshotPatch';
-import { renderBackground as render } from '../src/lifecycle/render';
import { LifecycleConstant } from '../src/lifecycleConstant';
import { CATCH_ERROR } from '../src/renderToOpcodes/constants';
import { __root } from '../src/root';
import { backgroundSnapshotInstanceManager, setupPage } from '../src/snapshot';
-import { elementTree, waitSchedule } from './utils/nativeMethod';
beforeAll(() => {
- options.debounceRendering = Promise.prototype.then.bind(Promise.resolve());
setupPage(__CreatePage('0', 0));
replaceCommitHook();
initDelayUnmount();
@@ -30,7 +28,6 @@ afterEach(() => {
globalEnvManager.resetEnv();
deinitGlobalSnapshotPatch();
vi.restoreAllMocks();
- elementTree.clear();
});
describe('useEffect', () => {
@@ -804,118 +801,4 @@ describe('useState', () => {
);
}
});
-
- it('should batch multiple updates', async function() {
- let _setCount;
- let _setCount2;
-
- const Child1 = () => {
- const [count, setCount] = useState(0);
- _setCount = setCount;
- return (
-
-
-
- );
- };
-
- const Child2 = () => {
- const [count, setCount] = useState(0);
- _setCount2 = setCount;
- return (
-
-
-
- );
- };
-
- const Comp = () => {
- return (
-
-
-
-
- );
- };
-
- // main thread render
- {
- __root.__jsx = ;
- renderPage();
- }
-
- // background render
- {
- globalEnvManager.switchToBackground();
- render(, __root);
- }
-
- // hydrate
- {
- // LifecycleConstant.firstScreen
- 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]);
- expect(globalThis.__OnLifecycleEvent).not.toBeCalled();
- }
-
- // insert node
- {
- globalEnvManager.switchToBackground();
- lynx.getNativeApp().callLepusMethod.mockClear();
- _setCount(1);
- _setCount2(2);
- await waitSchedule();
-
- expect(lynx.getNativeApp().callLepusMethod).toHaveBeenCalledTimes(1);
- expect(lynx.getNativeApp().callLepusMethod.mock.calls).toMatchInlineSnapshot(
- `
- [
- [
- "rLynxChange",
- {
- "data": "{"patchList":[{"id":42,"snapshotPatch":[3,-3,0,1]},{"id":43,"snapshotPatch":[3,-4,0,2]}]}",
- "patchOptions": {
- "reloadVersion": 0,
- },
- },
- [Function],
- ],
- ]
- `,
- );
- }
-
- {
- globalEnvManager.switchToMainThread();
- globalThis.__OnLifecycleEvent.mockClear();
- const rLynxChange = lynx.getNativeApp().callLepusMethod.mock.calls[0];
- globalThis[rLynxChange[0]](rLynxChange[1]);
- rLynxChange[2];
- lynx.getNativeApp().callLepusMethod.mockClear();
- await waitSchedule();
- expect(__root.__element_root).toMatchInlineSnapshot(`
-
-
-
-
-
-
-
-
-
-
- `);
- }
- });
});
diff --git a/packages/react/runtime/__test__/lynx/timing.test.jsx b/packages/react/runtime/__test__/lynx/timing.test.jsx
index 04601e217a..d4a575c0b7 100644
--- a/packages/react/runtime/__test__/lynx/timing.test.jsx
+++ b/packages/react/runtime/__test__/lynx/timing.test.jsx
@@ -3,12 +3,11 @@
// 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 { Component, options } from 'preact';
+import { Component, options, render } from 'preact';
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { replaceCommitHook } from '../../src/lifecycle/patch/commit';
import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread';
-import { renderBackground as render } from '../../src/lifecycle/render';
import '../../src/lynx/component';
import { initTimingAPI } from '../../src/lynx/performance';
import { __root } from '../../src/root';
diff --git a/packages/react/runtime/__test__/snapshot/gesture.test.jsx b/packages/react/runtime/__test__/snapshot/gesture.test.jsx
index a90dee9e38..9de53ab92e 100644
--- a/packages/react/runtime/__test__/snapshot/gesture.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/gesture.test.jsx
@@ -1,8 +1,8 @@
+import { render } from 'preact';
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
import { replaceCommitHook } from '../../src/lifecycle/patch/commit';
import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread';
-import { renderBackground as render } from '../../src/lifecycle/render';
import { __root } from '../../src/root';
import { setupPage } from '../../src/snapshot';
import { globalEnvManager } from '../utils/envManager';
diff --git a/packages/react/runtime/__test__/snapshot/ref.test.jsx b/packages/react/runtime/__test__/snapshot/ref.test.jsx
index c0421f3848..66e1251f8c 100644
--- a/packages/react/runtime/__test__/snapshot/ref.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/ref.test.jsx
@@ -3,13 +3,13 @@
// 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 { Component, createRef, root, useState } from '../../src/index';
import { delayedLifecycleEvents } from '../../src/lifecycle/event/delayLifecycleEvents';
import { clearCommitTaskId, replaceCommitHook } from '../../src/lifecycle/patch/commit';
import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread';
-import { renderBackground as render } from '../../src/lifecycle/render';
import { __pendingListUpdates } from '../../src/list';
import { __root } from '../../src/root';
import { setupPage } from '../../src/snapshot';
diff --git a/packages/react/runtime/__test__/snapshot/workletEvent.test.jsx b/packages/react/runtime/__test__/snapshot/workletEvent.test.jsx
index fb2202bbab..54712065ff 100644
--- a/packages/react/runtime/__test__/snapshot/workletEvent.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/workletEvent.test.jsx
@@ -3,12 +3,12 @@
// 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 { Component, useState } from '../../src/index';
import { replaceCommitHook } from '../../src/lifecycle/patch/commit';
import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread';
-import { renderBackground as render } from '../../src/lifecycle/render';
import { __root } from '../../src/root';
import { setupPage } from '../../src/snapshot';
import { globalEnvManager } from '../utils/envManager';
diff --git a/packages/react/runtime/__test__/snapshot/workletRef.test.jsx b/packages/react/runtime/__test__/snapshot/workletRef.test.jsx
index b7655c49a6..a4e78f0f2a 100644
--- a/packages/react/runtime/__test__/snapshot/workletRef.test.jsx
+++ b/packages/react/runtime/__test__/snapshot/workletRef.test.jsx
@@ -3,13 +3,13 @@
// 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 { createCompBG1, createCompBGList, createCompBGSpread } from './workletRefBG';
import { createCompMT1, createCompMTList, createCompMTSpread } from './workletRefMT';
import { replaceCommitHook } from '../../src/lifecycle/patch/commit';
import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread';
-import { renderBackground as render } from '../../src/lifecycle/render';
import { __root } from '../../src/root';
import { setupPage } from '../../src/snapshot';
import { globalEnvManager } from '../utils/envManager';
diff --git a/packages/react/runtime/__test__/worklet/workletRef.test.jsx b/packages/react/runtime/__test__/worklet/workletRef.test.jsx
index 9eb2079f3e..97b6fcc7d7 100644
--- a/packages/react/runtime/__test__/worklet/workletRef.test.jsx
+++ b/packages/react/runtime/__test__/worklet/workletRef.test.jsx
@@ -3,11 +3,11 @@
// 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 { replaceCommitHook } from '../../src/lifecycle/patch/commit';
import { injectUpdateMainThread } from '../../src/lifecycle/patch/updateMainThread';
-import { renderBackground as render } from '../../src/lifecycle/render';
import { __root } from '../../src/root';
import { setupPage } from '../../src/snapshot';
import { destroyWorklet } from '../../src/worklet/destroy';
diff --git a/packages/react/runtime/src/lifecycle/destroy.ts b/packages/react/runtime/src/lifecycle/destroy.ts
index bdbf4fa6c9..c37368685e 100644
--- a/packages/react/runtime/src/lifecycle/destroy.ts
+++ b/packages/react/runtime/src/lifecycle/destroy.ts
@@ -1,18 +1,19 @@
// Copyright 2024 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 { render } from 'preact';
+
import { __root } from '../root.js';
import { delayedEvents } from './event/delayEvents.js';
import { delayedLifecycleEvents } from './event/delayLifecycleEvents.js';
import { globalCommitTaskMap } from './patch/commit.js';
-import { renderBackground } from './render.js';
function destroyBackground(): void {
if (__PROFILE__) {
console.profile('destroyBackground');
}
- renderBackground(null, __root as any);
+ render(null, __root as any);
globalCommitTaskMap.forEach(task => {
task();
diff --git a/packages/react/runtime/src/lifecycle/patch/commit.ts b/packages/react/runtime/src/lifecycle/patch/commit.ts
index 296bd00ae0..2a50f4a768 100644
--- a/packages/react/runtime/src/lifecycle/patch/commit.ts
+++ b/packages/react/runtime/src/lifecycle/patch/commit.ts
@@ -30,11 +30,6 @@ let nextCommitTaskId = 1;
let globalBackgroundSnapshotInstancesToRemove: number[] = [];
-let patchesToCommit: Patch[] = [];
-function clearPatchesToCommit(): void {
- patchesToCommit = [];
-}
-
interface Patch {
id: number;
snapshotPatch?: SnapshotPatch;
@@ -53,28 +48,6 @@ interface PatchOptions {
}
function replaceCommitHook(): void {
- // use our own `options.debounceRendering` to insert a timing flag before render
- type DebounceRendering = (f: () => void) => void;
- const injectDebounceRendering = (debounceRendering: DebounceRendering): DebounceRendering => {
- return (f: () => void) => {
- debounceRendering(() => {
- f();
- void commitToMainThread();
- });
- };
- };
- const defaultDebounceRendering = options.debounceRendering?.bind(options)
- ?? (Promise.prototype.then.bind(Promise.resolve()) as DebounceRendering);
- let _debounceRendering = injectDebounceRendering(defaultDebounceRendering);
- Object.defineProperty(options, 'debounceRendering', {
- get() {
- return _debounceRendering;
- },
- set(debounceRendering: DebounceRendering) {
- _debounceRendering = injectDebounceRendering(debounceRendering);
- },
- });
-
const oldCommit = options[COMMIT];
const commit = async (vnode: VNode, commitQueue: any[]) => {
if (__LEPUS__) {
@@ -82,6 +55,9 @@ function replaceCommitHook(): void {
commitQueue.length = 0;
return;
}
+
+ markTimingLegacy(PerformanceTimingKeys.updateDiffVdomEnd);
+ markTiming(PerformanceTimingKeys.diffVdomEnd);
const renderCallbacks = commitQueue.map((component: Component) => {
const ret = {
component,
@@ -121,7 +97,9 @@ function replaceCommitHook(): void {
});
const snapshotPatch = takeGlobalSnapshotPatch();
+ const flushOptions = globalFlushOptions;
const workletRefInitValuePatch = takeWorkletRefInitValuePatch();
+ globalFlushOptions = {};
if (!snapshotPatch && workletRefInitValuePatch.length === 0) {
// before hydration, skip patch
return;
@@ -137,44 +115,23 @@ function replaceCommitHook(): void {
if (workletRefInitValuePatch.length) {
patch.workletRefInitValuePatch = workletRefInitValuePatch;
}
+ const patchList: PatchList = {
+ patchList: [patch],
+ };
+ if (!isEmptyObject(flushOptions)) {
+ patchList.flushOptions = flushOptions;
+ }
+ const obj = commitPatchUpdate(patchList, {});
- patchesToCommit.push(patch);
- };
- options[COMMIT] = commit as ((...args: Parameters) => void);
-}
-
-async function commitToMainThread(): Promise {
- if (patchesToCommit.length === 0) {
- return;
- }
-
- markTimingLegacy(PerformanceTimingKeys.updateDiffVdomEnd);
- markTiming(PerformanceTimingKeys.diffVdomEnd);
-
- const flushOptions = globalFlushOptions;
- globalFlushOptions = {};
-
- const patchList: PatchList = {
- patchList: patchesToCommit,
- };
- patchesToCommit = [];
-
- if (!isEmptyObject(flushOptions)) {
- patchList.flushOptions = flushOptions;
- }
-
- const obj = commitPatchUpdate(patchList, {});
-
- lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
- for (let i = 0; i < patchList.patchList.length; i++) {
- const patch = patchList.patchList[i]!;
- const commitTask = globalCommitTaskMap.get(patch.id);
+ lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
+ const commitTask = globalCommitTaskMap.get(commitTaskId);
if (commitTask) {
commitTask();
- globalCommitTaskMap.delete(patch.id);
+ globalCommitTaskMap.delete(commitTaskId);
}
- }
- });
+ });
+ };
+ options[COMMIT] = commit as ((...args: Parameters) => void);
}
function commitPatchUpdate(patchList: PatchList, patchOptions: Omit): {
@@ -232,15 +189,12 @@ function replaceRequestAnimationFrame(): void {
*/
export {
commitPatchUpdate,
- commitToMainThread,
genCommitTaskId,
clearCommitTaskId,
globalBackgroundSnapshotInstancesToRemove,
globalCommitTaskMap,
globalFlushOptions,
nextCommitTaskId,
- patchesToCommit,
- clearPatchesToCommit,
replaceCommitHook,
replaceRequestAnimationFrame,
type PatchOptions,
diff --git a/packages/react/runtime/src/lifecycle/reload.ts b/packages/react/runtime/src/lifecycle/reload.ts
index 9f7afb7ee2..b8d1bf9f03 100644
--- a/packages/react/runtime/src/lifecycle/reload.ts
+++ b/packages/react/runtime/src/lifecycle/reload.ts
@@ -1,6 +1,8 @@
// Copyright 2024 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 { render } from 'preact';
+
import { hydrate } from '../hydrate.js';
import { LifecycleConstant } from '../lifecycleConstant.js';
import { __pendingListUpdates } from '../list.js';
@@ -13,7 +15,7 @@ import { destroyWorklet } from '../worklet/destroy.js';
import { clearJSReadyEventIdSwap, isJSReady } from './event/jsReady.js';
import { increaseReloadVersion } from './pass.js';
import { deinitGlobalSnapshotPatch } from './patch/snapshotPatch.js';
-import { renderBackground, renderMainThread } from './render.js';
+import { renderMainThread } from './render.js';
function reloadMainThread(data: any, options: UpdatePageOption): void {
if (__PROFILE__) {
@@ -74,7 +76,7 @@ function reloadBackground(updateData: Record): void {
// COW when modify `lynx.__initData` to make sure Provider & Consumer works
lynx.__initData = Object.assign({}, lynx.__initData, updateData);
- renderBackground(__root.__jsx, __root as any);
+ render(__root.__jsx, __root as any);
if (__PROFILE__) {
console.profileEnd();
diff --git a/packages/react/runtime/src/lifecycle/render.ts b/packages/react/runtime/src/lifecycle/render.ts
index 1c67d0e547..c7823a72fa 100644
--- a/packages/react/runtime/src/lifecycle/render.ts
+++ b/packages/react/runtime/src/lifecycle/render.ts
@@ -2,12 +2,10 @@
// 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 type { ComponentChild, ContainerNode } from 'preact';
import { renderOpcodesInto } from '../opcodes.js';
import { render as renderToString } from '../renderToOpcodes/index.js';
import { __root } from '../root.js';
-import { commitToMainThread } from './patch/commit.js';
function renderMainThread(): void {
/* v8 ignore start */
@@ -47,9 +45,4 @@ function renderMainThread(): void {
/* v8 ignore stop */
}
-function renderBackground(vnode: ComponentChild, parent: ContainerNode): void {
- render(vnode, parent);
- void commitToMainThread();
-}
-
-export { renderMainThread, renderBackground };
+export { renderMainThread };
diff --git a/packages/react/runtime/src/lynx-api.ts b/packages/react/runtime/src/lynx-api.ts
index 25b274c0bd..3c369b795a 100644
--- a/packages/react/runtime/src/lynx-api.ts
+++ b/packages/react/runtime/src/lynx-api.ts
@@ -1,13 +1,13 @@
// Copyright 2024 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 { render } from 'preact';
import { createContext, createElement } from 'preact/compat';
import { useState } from 'preact/hooks';
import type { Consumer, FC, ReactNode } from 'react';
import { factory, withInitDataInState } from './compat/initData.js';
import { useLynxGlobalEventListener } from './hooks/useLynxGlobalEventListener.js';
-import { renderBackground } from './lifecycle/render.js';
import { LifecycleConstant } from './lifecycleConstant.js';
import { flushDelayedLifecycleEvents } from './lynx/tt.js';
import { __root } from './root.js';
@@ -86,7 +86,7 @@ export const root: Root = {
__root.__jsx = jsx;
} else {
__root.__jsx = jsx;
- renderBackground(jsx, __root as any);
+ render(jsx, __root as any);
if (__FIRST_SCREEN_SYNC_TIMING__ === 'immediately') {
// This is for cases where `root.render()` is called asynchronously,
// `firstScreen` message might have been reached.
diff --git a/packages/react/runtime/src/lynx/tt.ts b/packages/react/runtime/src/lynx/tt.ts
index 85306bb958..22679d9302 100644
--- a/packages/react/runtime/src/lynx/tt.ts
+++ b/packages/react/runtime/src/lynx/tt.ts
@@ -1,34 +1,30 @@
// Copyright 2024 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 { render } from 'preact';
+
import { LifecycleConstant, NativeUpdateDataType } from '../lifecycleConstant.js';
import {
- PerformanceTimingKeys,
PerformanceTimingFlags,
+ PerformanceTimingKeys,
PipelineOrigins,
beginPipeline,
markTiming,
} from './performance.js';
import { BackgroundSnapshotInstance, hydrate } from '../backgroundSnapshot.js';
+import { runWithForce } from './runWithForce.js';
import { destroyBackground } from '../lifecycle/destroy.js';
import { delayedEvents, delayedPublishEvent } from '../lifecycle/event/delayEvents.js';
import { delayLifecycleEvent, delayedLifecycleEvents } from '../lifecycle/event/delayLifecycleEvents.js';
-import {
- clearPatchesToCommit,
- commitPatchUpdate,
- genCommitTaskId,
- globalCommitTaskMap,
- patchesToCommit,
- type PatchList,
-} from '../lifecycle/patch/commit.js';
+import { commitPatchUpdate, genCommitTaskId, globalCommitTaskMap } from '../lifecycle/patch/commit.js';
+import type { PatchList } from '../lifecycle/patch/commit.js';
import { reloadBackground } from '../lifecycle/reload.js';
-import { renderBackground } from '../lifecycle/render.js';
import { CHILDREN } from '../renderToOpcodes/constants.js';
import { __root } from '../root.js';
import { globalRefsToSet, updateBackgroundRefs } from '../snapshot/ref.js';
import { backgroundSnapshotInstanceManager } from '../snapshot.js';
import { destroyWorklet } from '../worklet/destroy.js';
-import { runWithForce } from './runWithForce.js';
+
export { runWithForce };
function injectTt(): void {
@@ -129,14 +125,11 @@ function onLifecycleEventImpl(type: string, data: any): void {
console.profile('commitChanges');
}
const commitTaskId = genCommitTaskId();
- patchesToCommit.push(
- { snapshotPatch, id: commitTaskId },
- );
const patchList: PatchList = {
- patchList: patchesToCommit,
+ patchList: [{ snapshotPatch, id: commitTaskId }],
};
- clearPatchesToCommit();
const obj = commitPatchUpdate(patchList, { isHydration: true });
+
lynx.getNativeApp().callLepusMethod(LifecycleConstant.patchUpdate, obj, () => {
updateBackgroundRefs(commitTaskId);
globalCommitTaskMap.forEach((commitTask, id) => {
@@ -212,7 +205,7 @@ function updateGlobalProps(newData: Record): void {
// This is already done because updateFromRoot will consume all dirty flags marked by
// the setState, and setState's flush will be a noop. No extra diffs will be needed.
Promise.resolve().then(() => {
- runWithForce(() => renderBackground(__root.__jsx, __root as any));
+ runWithForce(() => render(__root.__jsx, __root as any));
});
lynxCoreInject.tt.GlobalEventEmitter.emit('onGlobalPropsChanged');
}
diff --git a/packages/react/testing-library/src/__tests__/worklet.test.jsx b/packages/react/testing-library/src/__tests__/worklet.test.jsx
index 221a73a413..b17ffeda7f 100644
--- a/packages/react/testing-library/src/__tests__/worklet.test.jsx
+++ b/packages/react/testing-library/src/__tests__/worklet.test.jsx
@@ -441,7 +441,24 @@ describe('worklet', () => {
[
"rLynxChange",
{
- "data": "{"patchList":[{"id":1,"workletRefInitValuePatch":[[1,null],[2,0]]},{"snapshotPatch":[3,-2,0,{"_wvid":1},3,-2,1,{"_c":{"ref":{"_wvid":1},"num":{"_wvid":2}},"_wkltId":"a45f:test:9","_execId":1}],"id":2}]}",
+ "data": "{"patchList":[{"id":1,"workletRefInitValuePatch":[[1,null],[2,0]]}]}",
+ "patchOptions": {
+ "pipelineOptions": {
+ "dsl": "reactLynx",
+ "needTimestamps": true,
+ "pipelineID": "pipelineID",
+ "pipelineOrigin": "reactLynxHydrate",
+ "stage": "hydrate",
+ },
+ "reloadVersion": 0,
+ },
+ },
+ [Function],
+ ],
+ [
+ "rLynxChange",
+ {
+ "data": "{"patchList":[{"snapshotPatch":[3,-2,0,{"_wvid":1},3,-2,1,{"_c":{"ref":{"_wvid":1},"num":{"_wvid":2}},"_wkltId":"a45f:test:9","_execId":1}],"id":2}]}",
"patchOptions": {
"isHydration": true,
"pipelineOptions": {
diff --git a/packages/react/testing-library/src/pure.jsx b/packages/react/testing-library/src/pure.jsx
index cad285ac72..92064e9346 100644
--- a/packages/react/testing-library/src/pure.jsx
+++ b/packages/react/testing-library/src/pure.jsx
@@ -8,9 +8,9 @@ import { act } from 'preact/test-utils';
import { __root } from '@lynx-js/react/internal';
+import { commitToMainThread } from '../../runtime/lib/lifecycle/patch/commit.js';
import { flushDelayedLifecycleEvents } from '../../runtime/lib/lynx/tt.js';
import { clearPage } from '../../runtime/lib/snapshot.js';
-import { commitToMainThread } from '../../runtime/lib/lifecycle/patch/commit.js';
export function waitSchedule() {
return new Promise(resolve => {
@@ -93,8 +93,6 @@ export function cleanup() {
globalThis.lynxEnv.switchToBackgroundThread();
act(() => {
preactRender(null, __root);
- // This is needed to ensure that the ui updates are sent to the main thread
- commitToMainThread();
});
lynxEnv.mainThread.elementTree.root = undefined;
diff --git a/packages/react/testing-library/src/vitest-global-setup.js b/packages/react/testing-library/src/vitest-global-setup.js
index d8dc92bb52..3cf3f44542 100644
--- a/packages/react/testing-library/src/vitest-global-setup.js
+++ b/packages/react/testing-library/src/vitest-global-setup.js
@@ -1,23 +1,21 @@
import { options } from 'preact';
-import { SnapshotInstance } from '../../runtime/lib/snapshot.js';
-import { snapshotInstanceManager } from '../../runtime/lib/snapshot.js';
+
import { BackgroundSnapshotInstance } from '../../runtime/lib/backgroundSnapshot.js';
-import { backgroundSnapshotInstanceManager } from '../../runtime/lib/snapshot.js';
-import { injectCalledByNative } from '../../runtime/lib/lynx/calledByNative.js';
+import { clearCommitTaskId, replaceCommitHook } from '../../runtime/lib/lifecycle/patch/commit.js';
+import { deinitGlobalSnapshotPatch } from '../../runtime/lib/lifecycle/patch/snapshotPatch.js';
import { injectUpdateMainThread } from '../../runtime/lib/lifecycle/patch/updateMainThread.js';
-import {
- replaceCommitHook,
- clearPatchesToCommit,
- clearCommitTaskId,
-} from '../../runtime/lib/lifecycle/patch/commit.js';
-import { injectTt } from '../../runtime/lib/lynx/tt.js';
+import { injectCalledByNative } from '../../runtime/lib/lynx/calledByNative.js';
+import { flushDelayedLifecycleEvents, injectTt } from '../../runtime/lib/lynx/tt.js';
import { setRoot } from '../../runtime/lib/root.js';
-import { deinitGlobalSnapshotPatch } from '../../runtime/lib/lifecycle/patch/snapshotPatch.js';
+import {
+ SnapshotInstance,
+ backgroundSnapshotInstanceManager,
+ snapshotInstanceManager,
+} from '../../runtime/lib/snapshot.js';
+import { destroyWorklet } from '../../runtime/lib/worklet/destroy.js';
import { initApiEnv } from '../../worklet-runtime/lib/api/lynxApi.js';
import { initEventListeners } from '../../worklet-runtime/lib/listeners.js';
import { initWorklet } from '../../worklet-runtime/lib/workletRuntime.js';
-import { destroyWorklet } from '../../runtime/lib/worklet/destroy.js';
-import { flushDelayedLifecycleEvents } from '../../runtime/lib/lynx/tt.js';
const {
onInjectMainThreadGlobals,
@@ -133,7 +131,6 @@ globalThis.onInjectBackgroundThreadGlobals = (target) => {
// re-init global snapshot patch to undefined
deinitGlobalSnapshotPatch();
- clearPatchesToCommit();
clearCommitTaskId();
};
globalThis.onResetLynxEnv = () => {
diff --git a/packages/rspeedy/plugin-react/package.json b/packages/rspeedy/plugin-react/package.json
index 0540869dd7..b046f6cd44 100644
--- a/packages/rspeedy/plugin-react/package.json
+++ b/packages/rspeedy/plugin-react/package.json
@@ -64,7 +64,7 @@
"typia-rspack-plugin": "2.0.1"
},
"peerDependencies": {
- "@lynx-js/react": "^0.103.0 || ^0.104.0 || ^0.105.0 || ^0.106.0 || ^0.107.0"
+ "@lynx-js/react": "^0.103.0 || ^0.104.0 || ^0.105.0 || ^0.106.0 || ^0.107.0 || ^0.108.0"
},
"peerDependenciesMeta": {
"@lynx-js/react": {
diff --git a/packages/web-platform/web-elements/src/XSwiper/XSwiper.ts b/packages/web-platform/web-elements/src/XSwiper/XSwiper.ts
index d964266744..e03a7fc912 100644
--- a/packages/web-platform/web-elements/src/XSwiper/XSwiper.ts
+++ b/packages/web-platform/web-elements/src/XSwiper/XSwiper.ts
@@ -134,12 +134,12 @@ export class XSwiper extends HTMLElement {
minOffsetToMid,
};
}
- get current() {
+ get currentIndex() {
return this.#getNeatestElementIndexAndDistanceToMid().current;
}
- set current(newval: number) {
+ set currentIndex(newval: number) {
// When current is specified and current is updated in bindchange, there is no need to respond to the update of current
- if (this.current === newval) {
+ if (this.currentIndex === newval) {
return;
}
@@ -185,28 +185,28 @@ export class XSwiper extends HTMLElement {
}
scrollToNext() {
- const current = this.current;
+ const current = this.currentIndex;
const count = this.childElementCount;
if (current === count - 1) {
const circularPlay = this.circularPlay;
if (circularPlay) {
- this.current = 0;
+ this.currentIndex = 0;
}
} else {
- this.current += 1;
+ this.currentIndex += 1;
}
}
scrollToPrevious() {
- const current = this.current;
+ const current = this.currentIndex;
const count = this.childElementCount;
if (current === 0) {
const circularPlay = this.circularPlay;
if (circularPlay) {
- this.current = count - 1;
+ this.currentIndex = count - 1;
}
} else {
- this.current == count - 1;
+ this.currentIndex = count - 1;
}
}
diff --git a/packages/web-platform/web-elements/src/XSwiper/XSwiperAutoScroll.ts b/packages/web-platform/web-elements/src/XSwiper/XSwiperAutoScroll.ts
index 7e4b0c0670..489b475576 100644
--- a/packages/web-platform/web-elements/src/XSwiper/XSwiperAutoScroll.ts
+++ b/packages/web-platform/web-elements/src/XSwiper/XSwiperAutoScroll.ts
@@ -23,7 +23,7 @@ export class XSwiperAutoScroll
#handleCurrentChange(newVal: string | null) {
const newval = Number(newVal);
if (!Number.isNaN(newval)) {
- this.#dom.current = newval;
+ this.#dom.currentIndex = newval;
}
}
diff --git a/packages/web-platform/web-elements/src/XSwiper/XSwiperCircular.ts b/packages/web-platform/web-elements/src/XSwiper/XSwiperCircular.ts
index 8e8f06f3b5..a6b1e5413d 100644
--- a/packages/web-platform/web-elements/src/XSwiper/XSwiperCircular.ts
+++ b/packages/web-platform/web-elements/src/XSwiper/XSwiperCircular.ts
@@ -163,7 +163,7 @@ export class XSwiperCircular
if (newVal !== null) {
this.#changeEventHandler({
detail: {
- current: this.#dom.current,
+ current: this.#dom.currentIndex,
isDragged: false,
__isFirstLayout: true,
},
diff --git a/packages/web-platform/web-elements/src/XSwiper/XSwiperEvents.ts b/packages/web-platform/web-elements/src/XSwiper/XSwiperEvents.ts
index 2a323df741..4e19051e78 100644
--- a/packages/web-platform/web-elements/src/XSwiper/XSwiperEvents.ts
+++ b/packages/web-platform/web-elements/src/XSwiper/XSwiperEvents.ts
@@ -77,7 +77,7 @@ export class XSwipeEvents
|| currentScrollDistance < 10
|| Math.abs(currentScrollDistance - totalScrollDistance) <= pageLength
) {
- const current = this.#dom.current;
+ const current = this.#dom.currentIndex;
if (current !== this.#current) {
this.#dom.dispatchEvent(
new CustomEvent('change', {
diff --git a/packages/web-platform/web-elements/src/XSwiper/XSwiperIndicator.ts b/packages/web-platform/web-elements/src/XSwiper/XSwiperIndicator.ts
index a547f2cb05..e992003319 100644
--- a/packages/web-platform/web-elements/src/XSwiper/XSwiperIndicator.ts
+++ b/packages/web-platform/web-elements/src/XSwiper/XSwiperIndicator.ts
@@ -139,7 +139,7 @@ export class XSwiperIndicator
boostedQueueMicrotask(() => {
(
this.#getIndicatorContainer().children[
- this.#dom.current
+ this.#dom.currentIndex
] as HTMLElement
)?.style.setProperty(
'background-color',
diff --git a/packages/web-platform/web-elements/src/XSwiper/x-swiper.css b/packages/web-platform/web-elements/src/XSwiper/x-swiper.css
index 9b32e9fae3..8bc00978f7 100644
--- a/packages/web-platform/web-elements/src/XSwiper/x-swiper.css
+++ b/packages/web-platform/web-elements/src/XSwiper/x-swiper.css
@@ -74,7 +74,7 @@ x-swiper-item {
flex-basis: var(--lynx-linear-weight-basis) !important;
}
-x-swiper-item:nth-child(n+5) {
+x-swiper-item:nth-child(n+20) {
contain: strict;
content-visibility: auto;
}
diff --git a/packages/web-platform/web-explorer/package.json b/packages/web-platform/web-explorer/package.json
index 2b7f49d1dd..893b92caa7 100644
--- a/packages/web-platform/web-explorer/package.json
+++ b/packages/web-platform/web-explorer/package.json
@@ -20,7 +20,8 @@
],
"scripts": {
"build": "rsbuild build",
- "dev": "rsbuild dev"
+ "dev": "rsbuild dev",
+ "build:rsdoctor": "RSDOCTOR=true rsbuild build"
},
"dependencies": {
"qr-scanner": "^1.4.2"
@@ -30,7 +31,9 @@
"@lynx-js/lynx-core": "0.1.2",
"@lynx-js/web-core": "workspace:*",
"@lynx-js/web-elements": "workspace:*",
+ "@lynx-js/web-platform-rsbuild-plugin": "workspace:*",
"@rsbuild/core": "catalog:rsbuild",
+ "@rsdoctor/rspack-plugin": "1.0.2",
"tslib": "^2.8.1"
}
}
diff --git a/packages/web-platform/web-explorer/rsbuild.config.ts b/packages/web-platform/web-explorer/rsbuild.config.ts
index cf391fe5ad..d1949b6d62 100644
--- a/packages/web-platform/web-explorer/rsbuild.config.ts
+++ b/packages/web-platform/web-explorer/rsbuild.config.ts
@@ -1,5 +1,7 @@
import { defineConfig } from '@rsbuild/core';
import { codecovWebpackPlugin } from '@codecov/webpack-plugin';
+import { pluginWebPlatform } from '@lynx-js/web-platform-rsbuild-plugin';
+import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin';
const codecovEnabled = !!process.env.CI;
console.info('codecov enabled:', codecovEnabled);
export default defineConfig({
@@ -7,6 +9,7 @@ export default defineConfig({
entry: {
index: './index.ts',
},
+ include: [/web/],
},
output: {
target: 'web',
@@ -14,6 +17,7 @@ export default defineConfig({
root: 'dist',
},
filenameHash: false,
+ overrideBrowserslist: ['Chrome > 118'],
},
dev: {
writeToDisk: true,
@@ -37,6 +41,12 @@ export default defineConfig({
sha: process.env.GITHUB_SHA,
},
}),
+ process.env.RSDOCTOR === 'true'
+ && new RsdoctorRspackPlugin({
+ supports: {
+ generateTileGraph: true,
+ },
+ }),
],
},
},
@@ -46,4 +56,5 @@ export default defineConfig({
},
profile: true,
},
+ plugins: [pluginWebPlatform({ polyfill: false })],
});
diff --git a/packages/web-platform/web-mainthread-apis/src/elementAPI/style/cssPropertyMap.ts b/packages/web-platform/web-mainthread-apis/src/elementAPI/style/cssPropertyMap.ts
index ab02f63e92..242ac1189c 100644
--- a/packages/web-platform/web-mainthread-apis/src/elementAPI/style/cssPropertyMap.ts
+++ b/packages/web-platform/web-mainthread-apis/src/elementAPI/style/cssPropertyMap.ts
@@ -18,218 +18,238 @@ export function camelize(str: string): string {
let index = 1;
const cssPropertyMap: Record<
number,
- { name: string; dashName: string; defaultValue: string }
+ { name: string; dashName: string; isX: boolean }
> = {};
const cssPropertyReverseMap: Record = {};
-const V = (name: string, defaultValue: string) => {
+const V = (name: string) => {
const k = index++;
- cssPropertyMap[k] = { name: camelize(name), dashName: name, defaultValue };
+ const isX = name.startsWith('-x-');
+ cssPropertyMap[k] = { name: camelize(name), dashName: name, isX };
cssPropertyReverseMap[name] = k;
};
-V('top', 'auto');
-V('left', 'auto');
-V('right', 'auto');
-V('bottom', 'auto');
-V('position', 'relative');
-V('box-sizing', 'auto');
-V('background-color', 'transparent');
-V('border-left-color', 'black');
-V('border-right-color', 'black');
-V('border-top-color', 'black');
-V('border-bottom-color', 'black');
-V('border-radius', '0px');
-V('border-top-left-radius', '0px');
-V('border-bottom-left-radius', '0px');
-V('border-top-right-radius', '0px');
-V('border-bottom-right-radius', '0px');
-V('border-width', '0px');
-V('border-left-width', '0px');
-V('border-right-width', '0px');
-V('border-top-width', '0px');
-V('border-bottom-width', '0px');
-V('color', 'black');
-V('opacity', '1');
-V('display', 'auto');
-V('overflow', 'hidden');
-V('height', 'auto');
-V('width', 'auto');
-V('max-width', 'auto');
-V('min-width', 'auto');
-V('max-height', 'auto');
-V('min-height', 'auto');
-V('padding', '0px');
-V('padding-left', '0px');
-V('padding-right', '0px');
-V('padding-top', '0px');
-V('padding-bottom', '0px');
-V('margin', '0px');
-V('margin-left', '0px');
-V('margin-right', '0px');
-V('margin-top', '0px');
-V('margin-bottom', '0px');
-V('white-space', 'normal');
-V('letter-spacing', '0px');
-V('text-align', 'start');
-V('line-height', '');
-V('text-overflow', 'clip');
-V('font-size', 'medium');
-V('font-weight', 'normal');
-V('flex', '0');
-V('flex-grow', '0');
-V('flex-shrink', '1');
-V('flex-basis', 'auto');
-V('flex-direction', 'row');
-V('flex-wrap', 'nowrap');
-V('align-items', 'stretch');
-V('align-self', 'stretch');
-V('align-content', 'stretch');
-V('justify-content', 'stretch');
-V('background', 'transparent, transparent');
-V('border-color', 'black');
-V('font-family', '');
-V('font-style', 'normal');
-V('transform', '');
-V('animation', '');
-V('animation-name', '');
-V('animation-duration', '');
-V('animation-timing-function', 'linear');
-V('animation-delay', '0s');
-V('animation-iteration-count', '1');
-V('animation-direction', 'normal');
-V('animation-fill-mode', 'none');
-V('animation-play-state', 'running');
-V('line-spacing', '0px');
-V('border-style', 'solid');
-V('order', '0');
-V('box-shadow', '');
-V('transform-origin', '');
-V('linear-orientation', 'vertical');
-V('linear-weight-sum', '0');
-V('linear-weight', '0');
-V('linear-gravity', 'none');
-V('linear-layout-gravity', 'none');
-V('layout-animation-create-duration', '0s');
-V('layout-animation-create-timing-function', 'linear');
-V('layout-animation-create-delay', '0s');
-V('layout-animation-create-property', 'opacity');
-V('layout-animation-delete-duration', '0s');
-V('layout-animation-delete-timing-function', 'linear');
-V('layout-animation-delete-delay', '0s');
-V('layout-animation-delete-property', 'opacity');
-V('layout-animation-update-duration', '0s');
-V('layout-animation-update-timing-function', 'linear');
-V('layout-animation-update-delay', '0s');
-V('adapt-font-size', '0');
-V('aspect-ratio', '');
-V('text-decoration', '');
-V('text-shadow', '');
-V('background-image', '');
-V('background-position', '');
-V('background-origin', 'border-box');
-V('background-repeat', 'no-repeat');
-V('background-size', '');
-V('border', '');
-V('visibility', 'visible');
-V('border-right', '');
-V('border-left', '');
-V('border-top', '');
-V('border-bottom', '');
-V('transition', '');
-V('transition-property', '');
-V('transition-duration', '');
-V('transition-delay', '');
-V('transition-timing-function', '');
-V('content', '');
-V('border-left-style', '');
-V('border-right-style', '');
-V('border-top-style', '');
-V('border-bottom-style', '');
-V('implicit-animation', 'true');
-V('overflow-x', 'hidden');
-V('overflow-y', 'hidden');
-V('word-break', 'normal');
-V('background-clip', 'border-box');
-V('outline', 'medium none black');
-V('outline-color', 'black');
-V('outline-style', 'black');
-V('outline-width', 'medium');
-V('vertical-align', 'default');
-V('caret-color', 'auto');
-V('direction', 'normal');
-V('relative-id', '-1');
-V('relative-align-top', '-1');
-V('relative-align-right', '-1');
-V('relative-align-bottom', '-1');
-V('relative-align-left', '-1');
-V('relative-top-of', '-1');
-V('relative-right-of', '-1');
-V('relative-bottom-of', '-1');
-V('relative-left-of', '-1');
-V('relative-layout-once', 'true');
-V('relative-center', 'none');
-V('enter-transition-name', '');
-V('exit-transition-name', '');
-V('pause-transition-name', '');
-V('resume-transition-name', '');
-V('flex-flow', 'row nowrap');
-V('z-index', '0');
-V('text-decoration-color', 'black');
-V('linear-cross-gravity', 'none');
-V('margin-inline-start', '0px');
-V('margin-inline-end', '0px');
-V('padding-inline-start', '0px');
-V('padding-inline-end', '0px');
-V('border-inline-start-color', 'black');
-V('border-inline-end-color', 'black');
-V('border-inline-start-width', '0px');
-V('border-inline-end-width', '0px');
-V('border-inline-start-style', '');
-V('border-inline-end-style', '');
-V('border-start-start-radius', '0px');
-V('border-end-start-radius', '0px');
-V('border-start-end-radius', '0px');
-V('border-end-end-radius', '0px');
-V('relative-align-inline-start', '-1');
-V('relative-align-inline-end', '-1');
-V('relative-inline-start-of', '-1');
-V('relative-inline-end-of', '-1');
-V('inset-inline-start', '0px');
-V('inset-inline-end', '0px');
-V('mask-image', '');
-V('grid-template-columns', '');
-V('grid-template-rows', '');
-V('grid-auto-columns', '');
-V('grid-auto-rows', '');
-V('grid-column-span', '');
-V('grid-row-span', '');
-V('grid-column-start', '');
-V('grid-column-end', '');
-V('grid-row-start', '');
-V('grid-row-end', '');
-V('grid-column-gap', '');
-V('grid-row-gap', '');
-V('justify-items', 'stretch');
-V('justify-self', 'auto');
-V('grid-auto-flow', 'row');
-V('filter', '');
-V('list-main-axis-gap', '0px');
-V('list-cross-axis-gap', '0px');
-V('linear-direction', 'column');
-V('perspective', 'none');
-V('cursor', 'default');
-V('text-indent', '0px');
-V('clip-path', '');
-V('text-stroke', '0px transparent');
-V('text-stroke-width', '0px');
-V('text-stroke-color', 'transparent');
-V('-x-auto-font-size', 'false');
-V('-x-auto-font-size-preset-sizes', '');
+V('top');
+V('left');
+V('right');
+V('bottom');
+V('position');
+V('box-sizing');
+V('background-color');
+V('border-left-color');
+V('border-right-color');
+V('border-top-color');
+V('border-bottom-color');
+V('border-radius');
+V('border-top-left-radius');
+V('border-bottom-left-radius');
+V('border-top-right-radius');
+V('border-bottom-right-radius');
+V('border-width');
+V('border-left-width');
+V('border-right-width');
+V('border-top-width');
+V('border-bottom-width');
+V('color');
+V('opacity');
+V('display');
+V('overflow');
+V('height');
+V('width');
+V('max-width');
+V('min-width');
+V('max-height');
+V('min-height');
+V('padding');
+V('padding-left');
+V('padding-right');
+V('padding-top');
+V('padding-bottom');
+V('margin');
+V('margin-left');
+V('margin-right');
+V('margin-top');
+V('margin-bottom');
+V('white-space');
+V('letter-spacing');
+V('text-align');
+V('line-height');
+V('text-overflow');
+V('font-size');
+V('font-weight');
+V('flex');
+V('flex-grow');
+V('flex-shrink');
+V('flex-basis');
+V('flex-direction');
+V('flex-wrap');
+V('align-items');
+V('align-self');
+V('align-content');
+V('justify-content');
+V('background');
+V('border-color');
+V('font-family');
+V('font-style');
+V('transform');
+V('animation');
+V('animation-name');
+V('animation-duration');
+V('animation-timing-function');
+V('animation-delay');
+V('animation-iteration-count');
+V('animation-direction');
+V('animation-fill-mode');
+V('animation-play-state');
+V('line-spacing');
+V('border-style');
+V('order');
+V('box-shadow');
+V('transform-origin');
+V('linear-orientation');
+V('linear-weight-sum');
+V('linear-weight');
+V('linear-gravity');
+V('linear-layout-gravity');
+V('layout-animation-create-duration');
+V('layout-animation-create-timing-function');
+V('layout-animation-create-delay');
+V('layout-animation-create-property');
+V('layout-animation-delete-duration');
+V('layout-animation-delete-timing-function');
+V('layout-animation-delete-delay');
+V('layout-animation-delete-property');
+V('layout-animation-update-duration');
+V('layout-animation-update-timing-function');
+V('layout-animation-update-delay');
+V('adapt-font-size');
+V('aspect-ratio');
+V('text-decoration');
+V('text-shadow');
+V('background-image');
+V('background-position');
+V('background-origin');
+V('background-repeat');
+V('background-size');
+V('border');
+V('visibility');
+V('border-right');
+V('border-left');
+V('border-top');
+V('border-bottom');
+V('transition');
+V('transition-property');
+V('transition-duration');
+V('transition-delay');
+V('transition-timing-function');
+V('content');
+V('border-left-style');
+V('border-right-style');
+V('border-top-style');
+V('border-bottom-style');
+V('implicit-animation');
+V('overflow-x');
+V('overflow-y');
+V('word-break');
+V('background-clip');
+V('outline');
+V('outline-color');
+V('outline-style');
+V('outline-width');
+V('vertical-align');
+V('caret-color');
+V('direction');
+V('relative-id');
+V('relative-align-top');
+V('relative-align-right');
+V('relative-align-bottom');
+V('relative-align-left');
+V('relative-top-of');
+V('relative-right-of');
+V('relative-bottom-of');
+V('relative-left-of');
+V('relative-layout-once');
+V('relative-center');
+V('enter-transition-name');
+V('exit-transition-name');
+V('pause-transition-name');
+V('resume-transition-name');
+V('flex-flow');
+V('z-index');
+V('text-decoration-color');
+V('linear-cross-gravity');
+V('margin-inline-start');
+V('margin-inline-end');
+V('padding-inline-start');
+V('padding-inline-end');
+V('border-inline-start-color');
+V('border-inline-end-color');
+V('border-inline-start-width');
+V('border-inline-end-width');
+V('border-inline-start-style');
+V('border-inline-end-style');
+V('border-start-start-radius');
+V('border-end-start-radius');
+V('border-start-end-radius');
+V('border-end-end-radius');
+V('relative-align-inline-start');
+V('relative-align-inline-end');
+V('relative-inline-start-of');
+V('relative-inline-end-of');
+V('inset-inline-start');
+V('inset-inline-end');
+V('mask-image');
+V('grid-template-columns');
+V('grid-template-rows');
+V('grid-auto-columns');
+V('grid-auto-rows');
+V('grid-column-span');
+V('grid-row-span');
+V('grid-column-start');
+V('grid-column-end');
+V('grid-row-start');
+V('grid-row-end');
+V('grid-column-gap');
+V('grid-row-gap');
+V('justify-items');
+V('justify-self');
+V('grid-auto-flow');
+V('filter');
+V('list-main-axis-gap');
+V('list-cross-axis-gap');
+V('linear-direction');
+V('perspective');
+V('cursor');
+V('text-indent');
+V('clip-path');
+V('text-stroke');
+V('text-stroke-width');
+V('text-stroke-color');
+V('-x-auto-font-size');
+V('-x-auto-font-size-preset-sizes');
+V('mask');
+V('mask-repeat');
+V('mask-position');
+V('mask-clip');
+V('mask-origin');
+V('mask-size');
+V('gap');
+V('column-gap');
+V('row-gap');
+V('image-rendering');
+V('hyphens');
+V('-x-app-region');
+V(
+ '-x-animation-color-interpolation',
+);
+V('-x-handle-color');
+V('-x-handle-size');
+V('offset-path');
+V('offset-distance');
export function queryCSSProperty(index: number): {
name: string;
- defaultValue: string;
dashName: string;
+ isX: boolean;
} {
return cssPropertyMap[index]!;
}
diff --git a/packages/web-platform/web-mainthread-apis/src/elementAPI/style/styleFunctions.ts b/packages/web-platform/web-mainthread-apis/src/elementAPI/style/styleFunctions.ts
index 5840a4da30..cf1810e06d 100644
--- a/packages/web-platform/web-mainthread-apis/src/elementAPI/style/styleFunctions.ts
+++ b/packages/web-platform/web-mainthread-apis/src/elementAPI/style/styleFunctions.ts
@@ -68,7 +68,13 @@ export function createStyleFunctions(
): void {
let dashName: string | undefined;
if (typeof key === 'number') {
- dashName = queryCSSProperty(key).dashName;
+ const queryResult = queryCSSProperty(key);
+ dashName = queryResult.dashName;
+ if (queryResult.isX) {
+ console.error(
+ `[lynx-web] css property: ${dashName} is not supported.`,
+ );
+ }
} else {
dashName = key;
}
diff --git a/packages/web-platform/web-rsbuild-plugin/README.md b/packages/web-platform/web-rsbuild-plugin/README.md
new file mode 100644
index 0000000000..ee619412c2
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/README.md
@@ -0,0 +1,14 @@
+# @lynx-js/web-platform-rsbuild-plugin
+
+Lynx3 Web Platform rsbuild plugin
+
+## Usage
+
+```javascript
+import { pluginWebPlatform } from '@lynx-js/web-platform-rsbuild-plugin';
+import { defineConfig } from '@rsbuild/core';
+
+export default defineConfig({
+ plugins: [pluginWebPlatform()],
+});
+```
diff --git a/packages/web-platform/web-rsbuild-plugin/api-extractor.json b/packages/web-platform/web-rsbuild-plugin/api-extractor.json
new file mode 100644
index 0000000000..65b0c69ca6
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/api-extractor.json
@@ -0,0 +1,6 @@
+/**
+ * Config file for API Extractor. For more info, please visit: https://api-extractor.com
+ */
+{
+ "extends": "../../../api-extractor.json",
+}
diff --git a/packages/web-platform/web-rsbuild-plugin/package.json b/packages/web-platform/web-rsbuild-plugin/package.json
new file mode 100644
index 0000000000..318b30b659
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "@lynx-js/web-platform-rsbuild-plugin",
+ "version": "0.0.0",
+ "private": false,
+ "description": "A rsbuild plugin for Web projects in Lynx Web Platform",
+ "keywords": [
+ "rsbuild",
+ "Lynx",
+ "Web Platform"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/lynx-family/lynx-stack.git",
+ "directory": "packages/web-platform/web-rsbuild-plugin"
+ },
+ "license": "Apache-2.0",
+ "type": "module",
+ "main": "dist/index.js",
+ "typings": "dist/index.d.ts",
+ "files": [
+ "dist",
+ "CHANGELOG.md",
+ "README.md"
+ ],
+ "scripts": {
+ "build": "rslib build",
+ "dev": "rslib build --watch",
+ "test": "vitest"
+ },
+ "devDependencies": {
+ "@microsoft/api-extractor": "catalog:",
+ "@rsbuild/core": "catalog:rsbuild",
+ "@rslib/core": "^0.6.7",
+ "typescript": "^5.8.3",
+ "vitest": "^3.1.2"
+ },
+ "peerDependencies": {
+ "@rsbuild/core": "*"
+ }
+}
diff --git a/packages/web-platform/web-rsbuild-plugin/rslib.config.ts b/packages/web-platform/web-rsbuild-plugin/rslib.config.ts
new file mode 100644
index 0000000000..92a6b11025
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/rslib.config.ts
@@ -0,0 +1,7 @@
+import { defineConfig } from '@rslib/core';
+
+export default defineConfig({
+ lib: [
+ { format: 'esm', syntax: 'es2022', dts: { bundle: true } },
+ ],
+});
diff --git a/packages/web-platform/web-rsbuild-plugin/src/index.ts b/packages/web-platform/web-rsbuild-plugin/src/index.ts
new file mode 100644
index 0000000000..9fbecff720
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/src/index.ts
@@ -0,0 +1,11 @@
+// Copyright 2024 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.
+
+/**
+ * @packageDocumentation
+ *
+ * A rsbuild plugin that integrates with Web Platform.
+ */
+
+export { pluginWebPlatform } from './pluginWebPlatform.js';
diff --git a/packages/web-platform/web-rsbuild-plugin/src/pluginWebPlatform.ts b/packages/web-platform/web-rsbuild-plugin/src/pluginWebPlatform.ts
new file mode 100644
index 0000000000..3aab4ad959
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/src/pluginWebPlatform.ts
@@ -0,0 +1,67 @@
+// Copyright 2024 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 type { RsbuildPlugin } from '@rsbuild/core';
+
+/**
+ * The options for {@link pluginWebPlatform}.
+ *
+ * @public
+ */
+export interface PluginWebPlatformOptions {
+ /**
+ * Whether to polyfill the packages about Lynx Web Platform.
+ *
+ * If it is true, @lynx-js will be compiled and polyfills will be added
+ *
+ * @default true
+ */
+ polyfill?: boolean;
+}
+
+/**
+ * Create a rsbuild plugin for Lynx Web Platform.
+ *
+ * @example
+ * ```ts
+ * // rsbuild.config.ts
+ * import { pluginWebPlatform } from '@lynx-js/web-platform-rsbuild-plugin'
+ * import { defineConfig } from '@rsbuild/core';
+ *
+ * export default defineConfig({
+ * plugins: [pluginWebPlatform()],
+ * })
+ * ```
+ *
+ * @public
+ */
+export function pluginWebPlatform(
+ userOptions?: PluginWebPlatformOptions,
+): RsbuildPlugin {
+ return {
+ name: 'lynx:web-platform',
+ async setup(api) {
+ const defaultPluginOptions = {
+ polyfill: true,
+ };
+ const options = Object.assign({}, defaultPluginOptions, userOptions);
+
+ api.modifyRsbuildConfig(config => {
+ if (options.polyfill === true) {
+ config.source = {
+ ...config.source,
+ include: [
+ ...(config.source?.include ?? []),
+ /node_modules[\\/]@lynx-js[\\/]/,
+ ],
+ };
+ config.output = {
+ ...config.output,
+ polyfill: 'usage',
+ };
+ }
+ });
+ },
+ };
+}
diff --git a/packages/web-platform/web-rsbuild-plugin/tests/__snapshots__/config.test.ts.snap b/packages/web-platform/web-rsbuild-plugin/tests/__snapshots__/config.test.ts.snap
new file mode 100644
index 0000000000..0079db6849
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/tests/__snapshots__/config.test.ts.snap
@@ -0,0 +1,7 @@
+// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+
+exports[`Config > basic config 1`] = `
+[
+ /node_modules\\[\\\\\\\\/\\]@lynx-js\\[\\\\\\\\/\\]/,
+]
+`;
diff --git a/packages/web-platform/web-rsbuild-plugin/tests/config.test.ts b/packages/web-platform/web-rsbuild-plugin/tests/config.test.ts
new file mode 100644
index 0000000000..ced15c0de1
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/tests/config.test.ts
@@ -0,0 +1,21 @@
+import { createRsbuild } from '@rsbuild/core';
+import { describe, expect, test, vi } from 'vitest';
+import { pluginWebPlatform } from '../src/index.js';
+
+describe('Config', () => {
+ test('basic config', async () => {
+ const rsbuild = await createRsbuild({
+ rsbuildConfig: {
+ plugins: [
+ pluginWebPlatform(),
+ ],
+ },
+ });
+
+ await rsbuild.initConfigs();
+ const rsbuildConfig = rsbuild.getRsbuildConfig();
+
+ expect(rsbuildConfig.source?.include).toMatchSnapshot();
+ expect(rsbuildConfig.output?.polyfill).toBe('usage');
+ });
+});
diff --git a/packages/web-platform/web-rsbuild-plugin/tsconfig.json b/packages/web-platform/web-rsbuild-plugin/tsconfig.json
new file mode 100644
index 0000000000..cffb9f9080
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "rootDir": "./src",
+ "outDir": "./dist",
+ "lib": ["ESNext"],
+ "noUnusedParameters": false,
+ "noImplicitReturns": false,
+ },
+ "include": ["src"],
+}
diff --git a/packages/web-platform/web-rsbuild-plugin/turbo.json b/packages/web-platform/web-rsbuild-plugin/turbo.json
new file mode 100644
index 0000000000..c73c3199b8
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/turbo.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "https://turbo.build/schema.json",
+ "extends": [
+ "//"
+ ],
+ "tasks": {
+ "build": {
+ "dependsOn": [
+ "^build"
+ ],
+ "inputs": [
+ "src"
+ ],
+ "outputs": [
+ "dist"
+ ]
+ }
+ }
+}
diff --git a/packages/web-platform/web-rsbuild-plugin/vitest.config.ts b/packages/web-platform/web-rsbuild-plugin/vitest.config.ts
new file mode 100644
index 0000000000..0658fa4d5c
--- /dev/null
+++ b/packages/web-platform/web-rsbuild-plugin/vitest.config.ts
@@ -0,0 +1,15 @@
+import { defineProject } from 'vitest/config';
+import type { UserWorkspaceConfig } from 'vitest/config';
+
+const config: UserWorkspaceConfig = defineProject({
+ test: {
+ name: 'web-platform',
+ expect: {
+ poll: {
+ timeout: 3000,
+ },
+ },
+ },
+});
+
+export default config;
diff --git a/packages/webpack/react-webpack-plugin/package.json b/packages/webpack/react-webpack-plugin/package.json
index b74dbced79..bb16121c16 100644
--- a/packages/webpack/react-webpack-plugin/package.json
+++ b/packages/webpack/react-webpack-plugin/package.json
@@ -53,7 +53,7 @@
"webpack": "^5.99.6"
},
"peerDependencies": {
- "@lynx-js/react": "^0.103.0 || ^0.104.0 || ^0.105.0 || ^0.106.0 || ^0.107.0",
+ "@lynx-js/react": "^0.103.0 || ^0.104.0 || ^0.105.0 || ^0.106.0 || ^0.107.0 || ^0.108.0",
"@lynx-js/template-webpack-plugin": "^0.4.0 || ^0.5.0 || ^0.6.0"
},
"peerDependenciesMeta": {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a98551b5ba..f8a02320ad 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -699,9 +699,15 @@ importers:
'@lynx-js/web-elements':
specifier: workspace:*
version: link:../web-elements
+ '@lynx-js/web-platform-rsbuild-plugin':
+ specifier: workspace:*
+ version: link:../web-rsbuild-plugin
'@rsbuild/core':
specifier: catalog:rsbuild
version: 1.3.11
+ '@rsdoctor/rspack-plugin':
+ specifier: 1.0.2
+ version: 1.0.2(@rsbuild/core@1.3.11)(@rspack/core@1.3.6(@swc/helpers@0.5.17))(webpack@5.99.6)
tslib:
specifier: ^2.8.1
version: 2.8.1
@@ -721,6 +727,24 @@ importers:
specifier: ^1.1.0
version: 1.1.0
+ packages/web-platform/web-rsbuild-plugin:
+ devDependencies:
+ '@microsoft/api-extractor':
+ specifier: 'catalog:'
+ version: 7.52.5(@types/node@22.15.2)
+ '@rsbuild/core':
+ specifier: catalog:rsbuild
+ version: 1.3.11
+ '@rslib/core':
+ specifier: ^0.6.7
+ version: 0.6.7(@microsoft/api-extractor@7.52.5(@types/node@22.15.2))(typescript@5.8.3)
+ typescript:
+ specifier: ^5.8.3
+ version: 5.8.3
+ vitest:
+ specifier: ^3.1.2
+ version: 3.1.2(@types/debug@4.1.12)(@types/node@22.15.2)(@vitest/ui@3.1.2)(jsdom@26.1.0)(sass-embedded@1.86.0)(terser@5.31.6)
+
packages/web-platform/web-style-transformer: {}
packages/web-platform/web-tests: