diff --git a/.changeset/lazy-bundle-fetch-bundle.md b/.changeset/lazy-bundle-fetch-bundle.md
new file mode 100644
index 0000000000..d096c5ac56
--- /dev/null
+++ b/.changeset/lazy-bundle-fetch-bundle.md
@@ -0,0 +1,14 @@
+---
+"@lynx-js/react": minor
+"@lynx-js/react-rsbuild-plugin": minor
+"@lynx-js/react-webpack-plugin": minor
+"@lynx-js/template-webpack-plugin": minor
+---
+
+feat(lazy-bundle): add `lynx.fetchBundle`-based loader
+
+Opt in by setting `engineVersion: '3.8'` (or higher) in `pluginReactLynx`.
+Use `import('./X', { with: { mode: 'sync' | 'async' } })` to control whether
+the first screen blocks on a sync fetch. The lazy bundle's main-thread
+section is bytecoded by default (skipped in dev or when `DEBUG` includes
+`rspeedy`).
diff --git a/Cargo.lock b/Cargo.lock
index d675d5299a..4986b5aa1d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3220,6 +3220,7 @@ version = "0.1.0"
dependencies = [
"napi",
"napi-derive",
+ "once_cell",
"serde",
"serde_json",
"swc_core",
diff --git a/benchmark/react/package.json b/benchmark/react/package.json
index 414884ffa4..8dd47d91e9 100644
--- a/benchmark/react/package.json
+++ b/benchmark/react/package.json
@@ -40,7 +40,7 @@
"@lynx-js/rspeedy": "workspace:*",
"@lynx-js/trace-processor": "^0.0.1",
"@lynx-js/type-element-api": "0.0.3",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/codecov.yml b/codecov.yml
index 89e92302b8..02aa737666 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -27,9 +27,12 @@ coverage:
ignore:
- ".github/**"
- "codecov.yml"
+ - "examples/**"
- "packages/genui/**"
- "pnpm-lock.yaml"
- "rstest.config.ts"
+ - "**/__swc_snapshots__/**"
+ - "**/__snapshots__/**"
fixes:
- "/home/runner/_work/lynx-stack::"
diff --git a/examples/gesture/package.json b/examples/gesture/package.json
index a4a463829c..7a93147f17 100644
--- a/examples/gesture/package.json
+++ b/examples/gesture/package.json
@@ -17,7 +17,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/examples/motion/package.json b/examples/motion/package.json
index c1cf64fd75..676cdf345a 100644
--- a/examples/motion/package.json
+++ b/examples/motion/package.json
@@ -16,7 +16,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/examples/react-compiler/package.json b/examples/react-compiler/package.json
index 4ef711e246..12e16528dd 100644
--- a/examples/react-compiler/package.json
+++ b/examples/react-compiler/package.json
@@ -15,7 +15,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@rsbuild/plugin-babel": "1.1.0",
"@types/react": "^18.3.28",
"babel-plugin-react-compiler": "0.0.0-experimental-fe727a3-20250909"
diff --git a/examples/react-element-template/package.json b/examples/react-element-template/package.json
index 76620154f8..93bbf5f4b0 100644
--- a/examples/react-element-template/package.json
+++ b/examples/react-element-template/package.json
@@ -15,7 +15,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/examples/react-element/package.json b/examples/react-element/package.json
index cbdf632345..d35ed8dc48 100644
--- a/examples/react-element/package.json
+++ b/examples/react-element/package.json
@@ -15,7 +15,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/examples/react-externals/package.json b/examples/react-externals/package.json
index c5e9d0f26b..6fb115716c 100644
--- a/examples/react-externals/package.json
+++ b/examples/react-externals/package.json
@@ -20,7 +20,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28",
"cross-env": "^7.0.3"
}
diff --git a/examples/react-lazy-bundle-standalone/lynx.config.consumer.js b/examples/react-lazy-bundle-standalone/lynx.config.consumer.js
index 2d53d0577e..3fc6409cb8 100644
--- a/examples/react-lazy-bundle-standalone/lynx.config.consumer.js
+++ b/examples/react-lazy-bundle-standalone/lynx.config.consumer.js
@@ -9,6 +9,7 @@ import { detectLanHost, producerDevPort } from './demo-ports.js';
const projectRoot = path.dirname(fileURLToPath(import.meta.url));
const enableBundleAnalysis = !!process.env['RSPEEDY_BUNDLE_ANALYSIS'];
+const enableFetchBundle = !!process.env['LAZY_BUNDLE_FETCHBUNDLE'];
const producerHost = detectLanHost();
export default defineConfig({
@@ -35,7 +36,9 @@ export default defineConfig({
},
},
plugins: [
- pluginReactLynx(),
+ pluginReactLynx({
+ ...(enableFetchBundle ? { engineVersion: '3.8' } : {}),
+ }),
pluginQRCode({
schema(url) {
return `${url}?fullscreen=true`;
diff --git a/examples/react-lazy-bundle-standalone/lynx.config.producer.js b/examples/react-lazy-bundle-standalone/lynx.config.producer.js
index 060865b02a..c274fdc040 100644
--- a/examples/react-lazy-bundle-standalone/lynx.config.producer.js
+++ b/examples/react-lazy-bundle-standalone/lynx.config.producer.js
@@ -8,12 +8,15 @@ import { detectLanHost, producerDevPort } from './demo-ports.js';
const projectRoot = path.dirname(fileURLToPath(import.meta.url));
const enableBundleAnalysis = !!process.env['RSPEEDY_BUNDLE_ANALYSIS'];
+const enableFetchBundle = !!process.env['LAZY_BUNDLE_FETCHBUNDLE'];
const producerPublicPath = `http://${detectLanHost()}:${producerDevPort}/`;
export default defineConfig({
source: {
entry: {
LazyComponent: './src/LazyComponent.tsx',
+ LazyComponentSync: './src/LazyComponentSync.tsx',
+ LazyComponentAsync: './src/LazyComponentAsync.tsx',
add: './src/utils/add.ts',
minus: './src/utils/minus.ts',
dynamic: './src/utils/dynamic.ts',
@@ -35,6 +38,7 @@ export default defineConfig({
plugins: [
pluginReactLynx({
experimental_isLazyBundle: true,
+ ...(enableFetchBundle ? { engineVersion: '3.8' } : {}),
}),
],
environments: {
diff --git a/examples/react-lazy-bundle-standalone/package.json b/examples/react-lazy-bundle-standalone/package.json
index ca41ae104e..f81296a01f 100644
--- a/examples/react-lazy-bundle-standalone/package.json
+++ b/examples/react-lazy-bundle-standalone/package.json
@@ -6,12 +6,15 @@
"scripts": {
"build": "pnpm run --parallel \"/^build:(producer|consumer)$/\"",
"build:consumer": "rspeedy build --config lynx.config.consumer.js",
+ "build:fetchbundle": "cross-env LAZY_BUNDLE_FETCHBUNDLE=1 pnpm run build",
"build:producer": "rspeedy build --config lynx.config.producer.js",
"dev": "node scripts/serve.mjs dev",
"dev:consumer": "rspeedy dev --config lynx.config.consumer.js",
+ "dev:fetchbundle": "cross-env LAZY_BUNDLE_FETCHBUNDLE=1 node scripts/serve.mjs dev",
"dev:producer": "rspeedy dev --config lynx.config.producer.js",
"preview": "node scripts/serve.mjs preview",
"preview:consumer": "rspeedy preview --config lynx.config.consumer.js",
+ "preview:fetchbundle": "cross-env LAZY_BUNDLE_FETCHBUNDLE=1 node scripts/serve.mjs preview",
"preview:producer": "rspeedy preview --config lynx.config.producer.js"
},
"dependencies": {
@@ -22,7 +25,8 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
- "@types/react": "^18.3.28"
+ "@lynx-js/types": "3.10.2-alpha.0",
+ "@types/react": "^18.3.28",
+ "cross-env": "^7.0.3"
}
}
diff --git a/examples/react-lazy-bundle-standalone/src/App.tsx b/examples/react-lazy-bundle-standalone/src/App.tsx
index 566c4918ce..264d666ddb 100644
--- a/examples/react-lazy-bundle-standalone/src/App.tsx
+++ b/examples/react-lazy-bundle-standalone/src/App.tsx
@@ -4,13 +4,40 @@ import { createProducerBundleUrl } from './entry-url.js';
import './App.css';
-const LazyComponent = lazy(() =>
- import(createProducerBundleUrl('LazyComponent.lynx.bundle'), {
- with: {
- type: 'component',
- },
- })
-);
+let LazyComponentDemo: () => JSX.Element;
+if (__LAZY_BUNDLE_FETCHER__ === 'FetchBundle') {
+ const LazyComponentSync = lazy(() =>
+ import(createProducerBundleUrl('LazyComponentSync.lynx.bundle'), {
+ with: { type: 'component', mode: 'sync' },
+ })
+ );
+ const LazyComponentAsync = lazy(() =>
+ import(createProducerBundleUrl('LazyComponentAsync.lynx.bundle'), {
+ with: { type: 'component', mode: 'async' },
+ })
+ );
+ LazyComponentDemo = () => (
+ <>
+ Loading sync...}>
+
+
+ Loading async...}>
+
+
+ >
+ );
+} else {
+ const LazyComponent = lazy(() =>
+ import(createProducerBundleUrl('LazyComponent.lynx.bundle'), {
+ with: { type: 'component' },
+ })
+ );
+ LazyComponentDemo = () => (
+ Loading...}>
+
+
+ );
+}
export function App() {
useEffect(() => {
@@ -43,9 +70,7 @@ export function App() {
on Lynx
- Loading...}>
-
-
+
diff --git a/examples/react-lazy-bundle-standalone/src/LazyComponentAsync.css b/examples/react-lazy-bundle-standalone/src/LazyComponentAsync.css
new file mode 100644
index 0000000000..cdd57eba1c
--- /dev/null
+++ b/examples/react-lazy-bundle-standalone/src/LazyComponentAsync.css
@@ -0,0 +1,4 @@
+.LazyComponentAsync {
+ font-weight: 700;
+ color: cyan;
+}
diff --git a/examples/react-lazy-bundle-standalone/src/LazyComponentAsync.tsx b/examples/react-lazy-bundle-standalone/src/LazyComponentAsync.tsx
new file mode 100644
index 0000000000..eb0765ad8c
--- /dev/null
+++ b/examples/react-lazy-bundle-standalone/src/LazyComponentAsync.tsx
@@ -0,0 +1,9 @@
+import './LazyComponentAsync.css';
+
+export default function LazyComponentAsync() {
+ return (
+
+ LazyComponentAsync
+
+ );
+}
diff --git a/examples/react-lazy-bundle-standalone/src/LazyComponentSync.css b/examples/react-lazy-bundle-standalone/src/LazyComponentSync.css
new file mode 100644
index 0000000000..082c6a36f2
--- /dev/null
+++ b/examples/react-lazy-bundle-standalone/src/LazyComponentSync.css
@@ -0,0 +1,4 @@
+.LazyComponentSync {
+ font-weight: 700;
+ color: yellow;
+}
diff --git a/examples/react-lazy-bundle-standalone/src/LazyComponentSync.tsx b/examples/react-lazy-bundle-standalone/src/LazyComponentSync.tsx
new file mode 100644
index 0000000000..0f79cdf792
--- /dev/null
+++ b/examples/react-lazy-bundle-standalone/src/LazyComponentSync.tsx
@@ -0,0 +1,9 @@
+import './LazyComponentSync.css';
+
+export default function LazyComponentSync() {
+ return (
+
+ LazyComponentSync
+
+ );
+}
diff --git a/examples/react-lazy-bundle-standalone/src/entry-url.ts b/examples/react-lazy-bundle-standalone/src/entry-url.ts
index 1dff1852ac..4235546bb3 100644
--- a/examples/react-lazy-bundle-standalone/src/entry-url.ts
+++ b/examples/react-lazy-bundle-standalone/src/entry-url.ts
@@ -1,6 +1,8 @@
export function createProducerBundleUrl(bundleFileName: string): string {
- if (process.env.NODE_ENV === 'production') {
- return `http://${process.env.LYNX_STANDALONE_PRODUCER_HOST}:${process.env.LYNX_STANDALONE_PRODUCER_PORT}/${bundleFileName}`;
+ if (process.env['NODE_ENV'] === 'production') {
+ return `http://${process.env['LYNX_STANDALONE_PRODUCER_HOST']}:${
+ process.env['LYNX_STANDALONE_PRODUCER_PORT']
+ }/${bundleFileName}`;
}
return `${__webpack_public_path__}producer/${bundleFileName}`;
}
diff --git a/examples/react-lazy-bundle/lynx.config.js b/examples/react-lazy-bundle/lynx.config.js
index ed483a4677..9de9038606 100644
--- a/examples/react-lazy-bundle/lynx.config.js
+++ b/examples/react-lazy-bundle/lynx.config.js
@@ -1,12 +1,38 @@
+import os from 'node:os';
+
import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
import { defineConfig } from '@lynx-js/rspeedy';
const enableBundleAnalysis = !!process.env['RSPEEDY_BUNDLE_ANALYSIS'];
+const enableFetchBundle = !!process.env['LAZY_BUNDLE_FETCHBUNDLE'];
+
+function detectLanHost() {
+ for (const ifaces of Object.values(os.networkInterfaces())) {
+ for (const iface of ifaces ?? []) {
+ if (iface.family === 'IPv4' && !iface.internal) {
+ return iface.address;
+ }
+ }
+ }
+ throw new Error('No external IPv4 interface found for lazy bundle host.');
+}
+
+const port = Number(process.env['LYNX_LAZY_BUNDLE_PORT'] ?? '54173');
+const assetPrefix = `http://${detectLanHost()}:${port}/`;
export default defineConfig({
+ output: {
+ assetPrefix,
+ },
+ server: {
+ port,
+ strictPort: true,
+ },
plugins: [
- pluginReactLynx(),
+ pluginReactLynx({
+ ...(enableFetchBundle ? { engineVersion: '3.8' } : {}),
+ }),
pluginQRCode({
schema(url) {
// We use `?fullscreen=true` to open the page in LynxExplorer in full screen mode
diff --git a/examples/react-lazy-bundle/package.json b/examples/react-lazy-bundle/package.json
index 5240bc0837..234a69eca8 100644
--- a/examples/react-lazy-bundle/package.json
+++ b/examples/react-lazy-bundle/package.json
@@ -5,7 +5,11 @@
"type": "module",
"scripts": {
"build": "rspeedy build",
- "dev": "rspeedy dev"
+ "build:fetchbundle": "cross-env LAZY_BUNDLE_FETCHBUNDLE=1 rspeedy build",
+ "dev": "rspeedy dev",
+ "dev:fetchbundle": "cross-env LAZY_BUNDLE_FETCHBUNDLE=1 rspeedy dev",
+ "preview": "rspeedy preview",
+ "preview:fetchbundle": "cross-env LAZY_BUNDLE_FETCHBUNDLE=1 rspeedy preview"
},
"dependencies": {
"@lynx-js/react": "workspace:*"
@@ -15,7 +19,8 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
- "@types/react": "^18.3.28"
+ "@lynx-js/types": "3.10.2-alpha.0",
+ "@types/react": "^18.3.28",
+ "cross-env": "^7.0.3"
}
}
diff --git a/examples/react-lazy-bundle/src/App.tsx b/examples/react-lazy-bundle/src/App.tsx
index de023703f5..9450135e23 100644
--- a/examples/react-lazy-bundle/src/App.tsx
+++ b/examples/react-lazy-bundle/src/App.tsx
@@ -2,7 +2,32 @@ import { Suspense, lazy, useEffect } from '@lynx-js/react';
import './App.css';
-const LazyComponent = lazy(() => import('./LazyComponent.js'));
+let LazyComponentDemo: () => JSX.Element;
+if (__LAZY_BUNDLE_FETCHER__ === 'FetchBundle') {
+ const LazyComponentSync = lazy(() =>
+ import('./LazyComponentSync.js', { with: { mode: 'sync' } })
+ );
+ const LazyComponentAsync = lazy(() =>
+ import('./LazyComponentAsync.js', { with: { mode: 'async' } })
+ );
+ LazyComponentDemo = () => (
+ <>
+ Loading sync...}>
+
+
+ Loading async...}>
+
+
+ >
+ );
+} else {
+ const LazyComponent = lazy(() => import('./LazyComponent.js'));
+ LazyComponentDemo = () => (
+ Loading...}>
+
+
+ );
+}
export function App() {
useEffect(() => {
@@ -27,9 +52,7 @@ export function App() {
on Lynx
- Loading...}>
-
-
+
diff --git a/examples/react-lazy-bundle/src/LazyComponentAsync.css b/examples/react-lazy-bundle/src/LazyComponentAsync.css
new file mode 100644
index 0000000000..cdd57eba1c
--- /dev/null
+++ b/examples/react-lazy-bundle/src/LazyComponentAsync.css
@@ -0,0 +1,4 @@
+.LazyComponentAsync {
+ font-weight: 700;
+ color: cyan;
+}
diff --git a/examples/react-lazy-bundle/src/LazyComponentAsync.tsx b/examples/react-lazy-bundle/src/LazyComponentAsync.tsx
new file mode 100644
index 0000000000..eb0765ad8c
--- /dev/null
+++ b/examples/react-lazy-bundle/src/LazyComponentAsync.tsx
@@ -0,0 +1,9 @@
+import './LazyComponentAsync.css';
+
+export default function LazyComponentAsync() {
+ return (
+
+ LazyComponentAsync
+
+ );
+}
diff --git a/examples/react-lazy-bundle/src/LazyComponentSync.css b/examples/react-lazy-bundle/src/LazyComponentSync.css
new file mode 100644
index 0000000000..082c6a36f2
--- /dev/null
+++ b/examples/react-lazy-bundle/src/LazyComponentSync.css
@@ -0,0 +1,4 @@
+.LazyComponentSync {
+ font-weight: 700;
+ color: yellow;
+}
diff --git a/examples/react-lazy-bundle/src/LazyComponentSync.tsx b/examples/react-lazy-bundle/src/LazyComponentSync.tsx
new file mode 100644
index 0000000000..0f79cdf792
--- /dev/null
+++ b/examples/react-lazy-bundle/src/LazyComponentSync.tsx
@@ -0,0 +1,9 @@
+import './LazyComponentSync.css';
+
+export default function LazyComponentSync() {
+ return (
+
+ LazyComponentSync
+
+ );
+}
diff --git a/examples/react-main-thread-function/package.json b/examples/react-main-thread-function/package.json
index ec0d610657..c4028dbc45 100644
--- a/examples/react-main-thread-function/package.json
+++ b/examples/react-main-thread-function/package.json
@@ -16,7 +16,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/examples/react-ui-sourcemap/package.json b/examples/react-ui-sourcemap/package.json
index db95a865c3..197998b139 100644
--- a/examples/react-ui-sourcemap/package.json
+++ b/examples/react-ui-sourcemap/package.json
@@ -16,7 +16,7 @@
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
"@lynx-js/template-webpack-plugin": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/examples/react/package.json b/examples/react/package.json
index 86dc4ea715..eed948847e 100644
--- a/examples/react/package.json
+++ b/examples/react/package.json
@@ -16,7 +16,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/examples/tailwindcss/package.json b/examples/tailwindcss/package.json
index bef7860626..c88f06154c 100644
--- a/examples/tailwindcss/package.json
+++ b/examples/tailwindcss/package.json
@@ -19,7 +19,7 @@
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
"@lynx-js/tailwind-preset": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28",
"rsbuild-plugin-tailwindcss": "0.2.4",
"tailwindcss": "^3.4.19"
diff --git a/packages/genui/a2ui-playground/package.json b/packages/genui/a2ui-playground/package.json
index f009a1b720..459cb76dce 100644
--- a/packages/genui/a2ui-playground/package.json
+++ b/packages/genui/a2ui-playground/package.json
@@ -28,7 +28,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@rsbuild/core": "catalog:rsbuild",
"@rsbuild/plugin-react": "^1.4.5",
"@types/qrcode": "^1.5.5",
diff --git a/packages/genui/a2ui/package.json b/packages/genui/a2ui/package.json
index 1d0dcdcfb7..9da53c614a 100644
--- a/packages/genui/a2ui/package.json
+++ b/packages/genui/a2ui/package.json
@@ -89,7 +89,7 @@
"@lynx-js/a2ui-catalog-extractor": "workspace:*",
"@lynx-js/lynx-ui": "^3.130.0",
"@lynx-js/react": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@rstest/core": "catalog:rstest",
"@types/react": "^18.3.28"
},
diff --git a/packages/genui/openui/package.json b/packages/genui/openui/package.json
index 9b62bf3bd6..4ad8bbdfcc 100644
--- a/packages/genui/openui/package.json
+++ b/packages/genui/openui/package.json
@@ -21,7 +21,7 @@
},
"devDependencies": {
"@lynx-js/react": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28"
}
}
diff --git a/packages/i18n/i18next-translation-dedupe/package.json b/packages/i18n/i18next-translation-dedupe/package.json
index 94a69c0644..d0dd5fb145 100644
--- a/packages/i18n/i18next-translation-dedupe/package.json
+++ b/packages/i18n/i18next-translation-dedupe/package.json
@@ -38,7 +38,7 @@
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
"@lynx-js/template-webpack-plugin": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"i18next": "26.0.6",
"i18next-cli": "1.54.2",
"rsbuild-plugin-i18next-extractor": "0.2.1"
diff --git a/packages/lynx/gesture-runtime/package.json b/packages/lynx/gesture-runtime/package.json
index 7df386907a..e868d0f585 100644
--- a/packages/lynx/gesture-runtime/package.json
+++ b/packages/lynx/gesture-runtime/package.json
@@ -32,7 +32,7 @@
},
"devDependencies": {
"@lynx-js/react": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@testing-library/jest-dom": "^6.9.1",
"rsbuild-plugin-publint": "0.3.4"
},
diff --git a/packages/motion/package.json b/packages/motion/package.json
index 3a6920f4d5..dce0c24168 100644
--- a/packages/motion/package.json
+++ b/packages/motion/package.json
@@ -53,7 +53,7 @@
},
"devDependencies": {
"@lynx-js/react": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"rsbuild-plugin-publint": "0.3.4"
},
"peerDependencies": {
diff --git a/packages/react/package.json b/packages/react/package.json
index 3a914e28a4..5bedd50643 100644
--- a/packages/react/package.json
+++ b/packages/react/package.json
@@ -227,7 +227,7 @@
"preact": "npm:@lynx-js/internal-preact@10.29.1-20260424024911-12b794f"
},
"devDependencies": {
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@microsoft/api-extractor": "catalog:",
"@types/react": "^18.3.28"
},
diff --git a/packages/react/runtime/__test__/snapshot/lynx/lazy-bundle-fetchbundle.test.js b/packages/react/runtime/__test__/snapshot/lynx/lazy-bundle-fetchbundle.test.js
new file mode 100644
index 0000000000..f999e597e0
--- /dev/null
+++ b/packages/react/runtime/__test__/snapshot/lynx/lazy-bundle-fetchbundle.test.js
@@ -0,0 +1,534 @@
+// 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, beforeEach, describe, expect, test, vi } from 'vitest';
+
+/* global lynx */
+
+const TIMEOUT_SECONDS = 5;
+
+beforeEach(() => {
+ vi.resetModules();
+ vi.unstubAllGlobals().stubGlobal('__LAZY_BUNDLE_FETCHER__', 'FetchBundle');
+});
+
+afterEach(() => {
+ vi.unstubAllGlobals();
+});
+
+describe('loadLazyBundle (FetchBundle) — main thread sync', () => {
+ let fetchBundle;
+ let waitMock;
+ let loadScript;
+ let loadStyleSheet;
+ let adoptStyleSheet;
+
+ beforeEach(() => {
+ waitMock = vi.fn();
+ fetchBundle = vi.fn(() => ({ wait: waitMock }));
+ loadScript = vi.fn();
+ loadStyleSheet = vi.fn();
+ adoptStyleSheet = vi.fn();
+ vi
+ .stubGlobal('__LEPUS__', true)
+ .stubGlobal('__MAIN_THREAD__', true)
+ .stubGlobal('lynx', { fetchBundle, loadScript })
+ .stubGlobal('__LoadStyleSheet', loadStyleSheet)
+ .stubGlobal('__AdoptStyleSheet', adoptStyleSheet);
+ });
+
+ test('happy path: .wait → loadScript(main-thread) → CSS adopt → sync then', async () => {
+ waitMock.mockReturnValueOnce({ code: 0, url: 'cached-url' });
+ loadScript.mockReturnValueOnce({ default: 'MTChunk' });
+ loadStyleSheet.mockReturnValueOnce({ id: 'sheet' });
+
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+
+ expect(fetchBundle).toHaveBeenCalledWith('foo', {});
+ expect(waitMock).toHaveBeenCalledWith(TIMEOUT_SECONDS);
+ expect(loadScript).toHaveBeenCalledWith('main-thread', {
+ bundleName: 'cached-url',
+ });
+ expect(loadStyleSheet).toHaveBeenCalledWith('CSS', 'cached-url');
+ expect(adoptStyleSheet).toHaveBeenCalledWith({ id: 'sheet' });
+
+ let thenCalled = false;
+ promise.then((v) => {
+ expect(v).toEqual({ default: 'MTChunk' });
+ thenCalled = true;
+ });
+ expect(thenCalled).toBe(true);
+ });
+
+ test('async mode (mode !== sync) returns never-resolving promise', async () => {
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('async', () => loadLazyBundle('foo'));
+
+ expect(fetchBundle).not.toHaveBeenCalled();
+ promise.then(
+ () => expect.fail('should not resolve'),
+ () => expect.fail('should not reject'),
+ );
+ await Promise.resolve();
+ });
+
+ test('.wait throws → never-resolving', async () => {
+ waitMock.mockImplementationOnce(() => {
+ throw new Error('timeout');
+ });
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+
+ expect(loadScript).not.toHaveBeenCalled();
+ promise.then(
+ () => expect.fail('should not resolve'),
+ () => expect.fail('should not reject'),
+ );
+ await Promise.resolve();
+ });
+
+ test('response.code !== 0 → never-resolving', async () => {
+ waitMock.mockReturnValueOnce({ code: 1, url: 'x' });
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+
+ expect(loadScript).not.toHaveBeenCalled();
+ promise.then(
+ () => expect.fail('should not resolve'),
+ () => expect.fail('should not reject'),
+ );
+ await Promise.resolve();
+ });
+
+ test('loadScript throws → never-resolving', async () => {
+ waitMock.mockReturnValueOnce({ code: 0, url: 'x' });
+ loadScript.mockImplementationOnce(() => {
+ throw new Error('no MTS section');
+ });
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+
+ expect(adoptStyleSheet).not.toHaveBeenCalled();
+ promise.then(
+ () => expect.fail('should not resolve'),
+ () => expect.fail('should not reject'),
+ );
+ await Promise.resolve();
+ });
+
+ test('null stylesheet → chunk still resolved, no AdoptStyleSheet', async () => {
+ waitMock.mockReturnValueOnce({ code: 0, url: 'x' });
+ loadScript.mockReturnValueOnce({ default: 'C' });
+ loadStyleSheet.mockReturnValueOnce(null);
+
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+
+ expect(loadStyleSheet).toHaveBeenCalled();
+ expect(adoptStyleSheet).not.toHaveBeenCalled();
+
+ let resolved;
+ promise.then((v) => {
+ resolved = v;
+ });
+ expect(resolved).toEqual({ default: 'C' });
+ });
+});
+
+describe('loadLazyBundle (FetchBundle) — background sync', () => {
+ let fetchBundle;
+ let waitMock;
+ let loadScript;
+
+ beforeEach(() => {
+ waitMock = vi.fn();
+ fetchBundle = vi.fn(() => ({ wait: waitMock }));
+ loadScript = vi.fn();
+ vi
+ .stubGlobal('__LEPUS__', false)
+ .stubGlobal('__MAIN_THREAD__', false)
+ .stubGlobal('__BACKGROUND__', true)
+ .stubGlobal('__JS__', true)
+ .stubGlobal('lynx', { fetchBundle, loadScript });
+ });
+
+ test('happy path: .wait → loadScript(background) → sync then', async () => {
+ waitMock.mockReturnValueOnce({ code: 0, url: 'u' });
+ loadScript.mockReturnValueOnce({ default: 'BG' });
+
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+
+ expect(fetchBundle).toHaveBeenCalledWith('foo', {});
+ expect(loadScript).toHaveBeenCalledWith('background', { bundleName: 'u' });
+
+ let thenCalled = false;
+ promise.then((v) => {
+ expect(v).toEqual({ default: 'BG' });
+ thenCalled = true;
+ });
+ expect(thenCalled).toBe(true);
+ });
+
+ test('.wait throws → reject', async () => {
+ waitMock.mockImplementationOnce(() => {
+ throw new Error('timeout');
+ });
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+ await expect(promise).rejects.toThrow('timeout');
+ });
+
+ test('response.code !== 0 → reject with cause', async () => {
+ waitMock.mockReturnValueOnce({ code: 2, url: 'u' });
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+ await expect(promise).rejects.toMatchObject({
+ message: 'Lazy bundle load failed, schema: foo',
+ cause: '{"code":2,"url":"u"}',
+ });
+ });
+
+ test('loadScript throws → reject', async () => {
+ waitMock.mockReturnValueOnce({ code: 0, url: 'u' });
+ loadScript.mockImplementationOnce(() => {
+ throw new Error('boom');
+ });
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = withLazyBundleMode('sync', () => loadLazyBundle('foo'));
+ await expect(promise).rejects.toThrow('boom');
+ });
+
+ test('undefined response → reject (covers !response branch)', async () => {
+ waitMock.mockReturnValueOnce(undefined);
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(
+ withLazyBundleMode('sync', () => loadLazyBundle('foo')),
+ ).rejects.toThrow('Lazy bundle load failed, schema: foo');
+ });
+
+ test('.wait throws non-Error → wrapped reject', async () => {
+ waitMock.mockImplementationOnce(() => {
+ // eslint-disable-next-line no-throw-literal
+ throw 'string err';
+ });
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(
+ withLazyBundleMode('sync', () => loadLazyBundle('foo')),
+ ).rejects.toThrow('string err');
+ });
+
+ test('loadScript throws non-Error → wrapped reject', async () => {
+ waitMock.mockReturnValueOnce({ code: 0, url: 'u' });
+ loadScript.mockImplementationOnce(() => {
+ // eslint-disable-next-line no-throw-literal
+ throw 'load boom';
+ });
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(
+ withLazyBundleMode('sync', () => loadLazyBundle('foo')),
+ ).rejects.toThrow('load boom');
+ });
+});
+
+describe('loadLazyBundle (FetchBundle) — unreachable', () => {
+ test('throws when neither MT nor JS', async () => {
+ vi.resetModules();
+ vi.unstubAllGlobals()
+ .stubGlobal('__LAZY_BUNDLE_FETCHER__', 'FetchBundle')
+ .stubGlobal('__MAIN_THREAD__', false)
+ .stubGlobal('__LEPUS__', false)
+ .stubGlobal('__JS__', false)
+ .stubGlobal('__BACKGROUND__', false);
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ expect(() => loadLazyBundle('foo')).toThrow('unreachable');
+ });
+});
+
+describe('loadLazyBundle (FetchBundle) — background async (cb-as-readiness)', () => {
+ let fetchBundle;
+ let thenMock;
+ let loadScript;
+ let callLepusMethod;
+ let getNativeApp;
+
+ beforeEach(() => {
+ thenMock = vi.fn();
+ fetchBundle = vi.fn(() => ({ then: thenMock }));
+ loadScript = vi.fn();
+ callLepusMethod = vi.fn();
+ getNativeApp = vi.fn(() => ({ callLepusMethod }));
+ vi
+ .stubGlobal('__LEPUS__', false)
+ .stubGlobal('__MAIN_THREAD__', false)
+ .stubGlobal('__BACKGROUND__', true)
+ .stubGlobal('__JS__', true)
+ .stubGlobal('lynx', { fetchBundle, loadScript, getNativeApp });
+ });
+
+ test('happy path: .then → loadScript → callLepusMethod → cb resolves', async () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockReturnValueOnce({ default: 'BG' });
+ callLepusMethod.mockImplementationOnce((_name, _payload, cb) => cb());
+
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ const promise = loadLazyBundle('foo');
+
+ expect(callLepusMethod).toHaveBeenCalledWith(
+ 'rLynxPrepareLazyBundleMTS',
+ { url: 'foo' },
+ expect.any(Function),
+ );
+ await expect(promise).resolves.toEqual({ default: 'BG' });
+ });
+
+ test('cb only fires AFTER loadScript completes (sequencing)', async () => {
+ const events = [];
+ thenMock.mockImplementationOnce((cb) => {
+ events.push('then-start');
+ cb({ code: 0, url: 'u' });
+ events.push('then-end');
+ });
+ loadScript.mockImplementationOnce(() => {
+ events.push('loadScript');
+ return { default: 'BG' };
+ });
+ callLepusMethod.mockImplementationOnce((_n, _p, cb) => {
+ events.push('callLepusMethod-start');
+ cb();
+ events.push('callLepusMethod-cb');
+ });
+
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+
+ await loadLazyBundle('foo');
+ expect(events).toEqual([
+ 'then-start',
+ 'loadScript',
+ 'callLepusMethod-start',
+ 'callLepusMethod-cb',
+ 'then-end',
+ ]);
+ });
+
+ test('fetchBundle throws sync → reject', async () => {
+ fetchBundle.mockImplementationOnce(() => {
+ throw new Error('net');
+ });
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(loadLazyBundle('foo')).rejects.toThrow('net');
+ expect(callLepusMethod).not.toHaveBeenCalled();
+ });
+
+ test('response.code !== 0 → reject with cause, no loadScript', async () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 1, url: 'u' }));
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(loadLazyBundle('foo')).rejects.toMatchObject({
+ message: 'Lazy bundle load failed, schema: foo',
+ cause: '{"code":1,"url":"u"}',
+ });
+ expect(loadScript).not.toHaveBeenCalled();
+ expect(callLepusMethod).not.toHaveBeenCalled();
+ });
+
+ test('undefined response → reject (covers !response branch)', async () => {
+ thenMock.mockImplementationOnce((cb) => cb(undefined));
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(loadLazyBundle('foo')).rejects.toThrow(
+ 'Lazy bundle load failed, schema: foo',
+ );
+ });
+
+ test('fetchBundle throws non-Error sync → wrapped reject', async () => {
+ fetchBundle.mockImplementationOnce(() => {
+ // eslint-disable-next-line no-throw-literal
+ throw 'string err';
+ });
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(loadLazyBundle('foo')).rejects.toThrow('string err');
+ });
+
+ test('callLepusMethod throws non-Error → wrapped reject', async () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockReturnValueOnce({ default: 'BG' });
+ callLepusMethod.mockImplementationOnce(() => {
+ // eslint-disable-next-line no-throw-literal
+ throw 'lepus boom';
+ });
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(loadLazyBundle('foo')).rejects.toThrow('lepus boom');
+ });
+
+ test('loadScript throws non-Error → wrapped reject', async () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockImplementationOnce(() => {
+ // eslint-disable-next-line no-throw-literal
+ throw 'load boom';
+ });
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(loadLazyBundle('foo')).rejects.toThrow('load boom');
+ });
+
+ test('loadScript throws → reject, no callLepusMethod', async () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockImplementationOnce(() => {
+ throw new Error('boom');
+ });
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(loadLazyBundle('foo')).rejects.toThrow('boom');
+ expect(callLepusMethod).not.toHaveBeenCalled();
+ });
+
+ test('callLepusMethod throws → reject', async () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockReturnValueOnce({ default: 'BG' });
+ callLepusMethod.mockImplementationOnce(() => {
+ throw new Error('lepus down');
+ });
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ await expect(loadLazyBundle('foo')).rejects.toThrow('lepus down');
+ });
+});
+
+describe('mode + QueryComponent — dev throw', () => {
+ beforeEach(() => {
+ vi
+ .stubGlobal('__LEPUS__', false)
+ .stubGlobal('__MAIN_THREAD__', false)
+ .stubGlobal('__BACKGROUND__', true)
+ .stubGlobal('__JS__', true)
+ .stubGlobal('__LAZY_BUNDLE_FETCHER__', 'QueryComponent')
+ .stubGlobal('lynx', { QueryComponent: vi.fn() })
+ .stubGlobal('lynxCoreInject', { tt: { getDynamicComponentExports: vi.fn() } });
+ });
+
+ test('__DEV__ + mode set → throws', async () => {
+ vi.stubGlobal('__DEV__', true);
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ expect(() => withLazyBundleMode('sync', () => loadLazyBundle('foo'))).toThrow(
+ /requires FetchBundle/,
+ );
+ });
+
+ test('prod (__DEV__: false) + mode set → no throw, falls through to QueryComponent', async () => {
+ vi.stubGlobal('__DEV__', false);
+ const { withLazyBundleMode, loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ // Doesn't throw; still calls QueryComponent (callback never fires here, so promise pends).
+ expect(() => withLazyBundleMode('sync', () => loadLazyBundle('foo'))).not.toThrow();
+ expect(lynx.QueryComponent).toHaveBeenCalledWith('foo', expect.any(Function));
+ });
+
+ test('__DEV__ + no mode → no throw', async () => {
+ vi.stubGlobal('__DEV__', true);
+ const { loadLazyBundle } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ expect(() => loadLazyBundle('foo')).not.toThrow();
+ });
+});
+
+describe('withLazyBundleMode helper', () => {
+ beforeEach(() => {
+ vi
+ .stubGlobal('__LEPUS__', false)
+ .stubGlobal('__MAIN_THREAD__', false)
+ .stubGlobal('__BACKGROUND__', true)
+ .stubGlobal('__JS__', true);
+ });
+
+ test('restores prior mode after factory returns', async () => {
+ const { withLazyBundleMode } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ let inner;
+ withLazyBundleMode('sync', () => {
+ withLazyBundleMode('async', () => {
+ inner = 'async';
+ });
+ inner += '-then-sync';
+ });
+ expect(inner).toBe('async-then-sync');
+ });
+
+ test('restores prior mode even if factory throws', async () => {
+ const { withLazyBundleMode } = await import(
+ '../../../src/snapshot/lynx/lazy-bundle'
+ );
+ expect(() =>
+ withLazyBundleMode('sync', () => {
+ throw new Error('factory err');
+ })
+ ).toThrow('factory err');
+ // After exit, mode is restored (undefined). Use it again to confirm no leak.
+ let leaked;
+ withLazyBundleMode('async', () => {
+ leaked = 'async';
+ });
+ expect(leaked).toBe('async');
+ });
+});
diff --git a/packages/react/runtime/__test__/snapshot/lynx/lazy-bundle.test.js b/packages/react/runtime/__test__/snapshot/lynx/lazy-bundle.test.js
index 740462f72c..4191b0f39b 100644
--- a/packages/react/runtime/__test__/snapshot/lynx/lazy-bundle.test.js
+++ b/packages/react/runtime/__test__/snapshot/lynx/lazy-bundle.test.js
@@ -12,7 +12,12 @@ describe('loadLazyBundle', () => {
vi
.unstubAllGlobals()
.stubGlobal('__LEPUS__', true)
- .stubGlobal('__MAIN_THREAD__', true);
+ .stubGlobal('__MAIN_THREAD__', true)
+ // Force the QueryComponent fetcher path. Production builds get
+ // `__LAZY_BUNDLE_FETCHER__` stamped in by DefinePlugin (default
+ // `'FetchBundle'`); unit tests don't run DefinePlugin so the
+ // global is undefined here unless we stub it explicitly.
+ .stubGlobal('__LAZY_BUNDLE_FETCHER__', 'QueryComponent');
});
test('should not have lynx.loadLazyBundle when not used', () => {
@@ -213,6 +218,8 @@ describe('loadLazyBundle', () => {
.stubGlobal('__MAIN_THREAD__', false)
.stubGlobal('__BACKGROUND__', true)
.stubGlobal('__JS__', true)
+ // Force the QueryComponent fetcher path (see main-thread block).
+ .stubGlobal('__LAZY_BUNDLE_FETCHER__', 'QueryComponent')
.stubGlobal('lynx', { QueryComponent })
.stubGlobal('lynxCoreInject', { tt: { getDynamicComponentExports } });
});
diff --git a/packages/react/runtime/__test__/snapshot/lynx/prepareLazyBundleMTS.test.js b/packages/react/runtime/__test__/snapshot/lynx/prepareLazyBundleMTS.test.js
new file mode 100644
index 0000000000..8ec5abedea
--- /dev/null
+++ b/packages/react/runtime/__test__/snapshot/lynx/prepareLazyBundleMTS.test.js
@@ -0,0 +1,166 @@
+// 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, beforeEach, describe, expect, test, vi } from 'vitest';
+
+/* global lynx */
+
+// `prepareLazyBundleMTS` is the MT-side handler registered for the
+// `rLynxPrepareLazyBundleMTS` lifecycle. It runs after BG calls
+// `callLepusMethod`, with the bundle already in native cache (so
+// `lynx.fetchBundle(url, {}).then(cb)` fires sync on lepus).
+
+describe('prepareLazyBundleMTS handler', () => {
+ let fetchBundle;
+ let thenMock;
+ let loadScript;
+ let loadStyleSheet;
+ let adoptStyleSheet;
+ let processEvalResult;
+ let injectPrepareLazyBundleMTS;
+
+ beforeEach(async () => {
+ vi.resetModules();
+ vi.unstubAllGlobals();
+
+ thenMock = vi.fn();
+ fetchBundle = vi.fn(() => ({ then: thenMock }));
+ loadScript = vi.fn();
+ loadStyleSheet = vi.fn();
+ adoptStyleSheet = vi.fn();
+ processEvalResult = vi.fn();
+
+ vi
+ .stubGlobal('lynx', { fetchBundle, loadScript })
+ .stubGlobal('__LoadStyleSheet', loadStyleSheet)
+ .stubGlobal('__AdoptStyleSheet', adoptStyleSheet)
+ .stubGlobal('processEvalResult', processEvalResult);
+
+ ({ injectPrepareLazyBundleMTS } = await import(
+ '../../../src/snapshot/lynx/prepareLazyBundleMTS'
+ ));
+ injectPrepareLazyBundleMTS();
+ });
+
+ afterEach(() => {
+ delete globalThis.rLynxPrepareLazyBundleMTS;
+ vi.unstubAllGlobals();
+ });
+
+ function invoke(url) {
+ return globalThis.rLynxPrepareLazyBundleMTS({ url });
+ }
+
+ test('happy path: fetchBundle.then → loadScript(main-thread) → processEvalResult → CSS adopt', () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockReturnValueOnce({ chunk: 'x' });
+ loadStyleSheet.mockReturnValueOnce({ id: 'sheet' });
+
+ invoke('foo');
+
+ expect(fetchBundle).toHaveBeenCalledWith('foo', {});
+ expect(loadScript).toHaveBeenCalledWith('main-thread', { bundleName: 'u' });
+ expect(processEvalResult).toHaveBeenCalledWith(expect.any(Function), 'foo');
+ // The factory passed to processEvalResult returns the loaded chunk.
+ const factory = processEvalResult.mock.calls[0][0];
+ expect(factory()).toEqual({ chunk: 'x' });
+ expect(loadStyleSheet).toHaveBeenCalledWith('CSS', 'u');
+ expect(adoptStyleSheet).toHaveBeenCalledWith({ id: 'sheet' });
+ });
+
+ test('cache: second call with same url is a no-op', () => {
+ thenMock.mockImplementation((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockReturnValue({ chunk: 'x' });
+ loadStyleSheet.mockReturnValue(null);
+
+ invoke('foo');
+ invoke('foo');
+
+ expect(fetchBundle).toHaveBeenCalledTimes(1);
+ expect(loadScript).toHaveBeenCalledTimes(1);
+ expect(processEvalResult).toHaveBeenCalledTimes(1);
+ });
+
+ test('cache: different urls are not deduped', () => {
+ thenMock.mockImplementation((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockReturnValue({ chunk: 'x' });
+ loadStyleSheet.mockReturnValue(null);
+
+ invoke('foo');
+ invoke('bar');
+
+ expect(fetchBundle).toHaveBeenCalledTimes(2);
+ expect(fetchBundle).toHaveBeenNthCalledWith(1, 'foo', {});
+ expect(fetchBundle).toHaveBeenNthCalledWith(2, 'bar', {});
+ });
+
+ test('fetchBundle throws sync → silent skip, no side effects', () => {
+ fetchBundle.mockImplementationOnce(() => {
+ throw new Error('net');
+ });
+
+ expect(() => invoke('foo')).not.toThrow();
+ expect(loadScript).not.toHaveBeenCalled();
+ expect(processEvalResult).not.toHaveBeenCalled();
+ expect(loadStyleSheet).not.toHaveBeenCalled();
+ });
+
+ test('response.code !== 0 → silent skip', () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 1, url: 'u' }));
+
+ invoke('foo');
+
+ expect(loadScript).not.toHaveBeenCalled();
+ expect(processEvalResult).not.toHaveBeenCalled();
+ expect(loadStyleSheet).not.toHaveBeenCalled();
+ });
+
+ test('loadScript throws (BG-only bundle) → no processEvalResult, no CSS', () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockImplementationOnce(() => {
+ throw new Error('no MTS section');
+ });
+
+ expect(() => invoke('foo')).not.toThrow();
+ expect(processEvalResult).not.toHaveBeenCalled();
+ expect(loadStyleSheet).not.toHaveBeenCalled();
+ });
+
+ test('processEvalResult absent → still loadScript + CSS adopt', async () => {
+ // Re-import without processEvalResult global.
+ vi.resetModules();
+ delete globalThis.rLynxPrepareLazyBundleMTS;
+ vi.unstubAllGlobals();
+ thenMock = vi.fn((cb) => cb({ code: 0, url: 'u' }));
+ fetchBundle = vi.fn(() => ({ then: thenMock }));
+ loadScript = vi.fn(() => ({ chunk: 'x' }));
+ loadStyleSheet = vi.fn(() => ({ id: 'sheet' }));
+ adoptStyleSheet = vi.fn();
+ vi
+ .stubGlobal('lynx', { fetchBundle, loadScript })
+ .stubGlobal('__LoadStyleSheet', loadStyleSheet)
+ .stubGlobal('__AdoptStyleSheet', adoptStyleSheet);
+ // No processEvalResult stub.
+
+ const fresh = await import(
+ '../../../src/snapshot/lynx/prepareLazyBundleMTS'
+ );
+ fresh.injectPrepareLazyBundleMTS();
+
+ expect(() => globalThis.rLynxPrepareLazyBundleMTS({ url: 'foo' })).not.toThrow();
+ expect(loadScript).toHaveBeenCalled();
+ expect(adoptStyleSheet).toHaveBeenCalledWith({ id: 'sheet' });
+ });
+
+ test('null stylesheet → no AdoptStyleSheet call', () => {
+ thenMock.mockImplementationOnce((cb) => cb({ code: 0, url: 'u' }));
+ loadScript.mockReturnValueOnce({ chunk: 'x' });
+ loadStyleSheet.mockReturnValueOnce(null);
+
+ invoke('foo');
+
+ expect(loadStyleSheet).toHaveBeenCalled();
+ expect(adoptStyleSheet).not.toHaveBeenCalled();
+ });
+});
diff --git a/packages/react/runtime/lazy/internal.js b/packages/react/runtime/lazy/internal.js
index 5d0d3b475c..87baa179f2 100644
--- a/packages/react/runtime/lazy/internal.js
+++ b/packages/react/runtime/lazy/internal.js
@@ -49,6 +49,7 @@ export const {
updateListItemPlatformInfo,
updateWorkletRef,
withInitDataInState,
+ withLazyBundleMode,
wrapWithLynxComponent,
} = target[sExportsReactInternal];
diff --git a/packages/react/runtime/package.json b/packages/react/runtime/package.json
index 9d0bf2ac92..21a91c5493 100644
--- a/packages/react/runtime/package.json
+++ b/packages/react/runtime/package.json
@@ -24,7 +24,7 @@
"devDependencies": {
"@lynx-js/react": "workspace:*",
"@lynx-js/react-transform": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28",
"pretty-format": "^30.2.0"
},
diff --git a/packages/react/runtime/src/internal.ts b/packages/react/runtime/src/internal.ts
index 2b3f554567..bc3622a34b 100644
--- a/packages/react/runtime/src/internal.ts
+++ b/packages/react/runtime/src/internal.ts
@@ -71,7 +71,7 @@ export const __ComponentIsPolyfill: FC<{ is: string }> = /* @__PURE__ */ factory
loadLazyBundle,
);
-export { loadLazyBundle } from './snapshot/lynx/lazy-bundle.js';
+export { loadLazyBundle, withLazyBundleMode } from './snapshot/lynx/lazy-bundle.js';
export { transformToWorklet } from './snapshot/worklet/call/transformToWorklet.js';
export { registerWorkletOnBackground } from './snapshot/worklet/hmr.js';
diff --git a/packages/react/runtime/src/lynx.ts b/packages/react/runtime/src/lynx.ts
index e4a20519af..8993a561e1 100644
--- a/packages/react/runtime/src/lynx.ts
+++ b/packages/react/runtime/src/lynx.ts
@@ -19,6 +19,7 @@ import { injectCalledByNative } from './snapshot/lynx/calledByNative.js';
import { setupLynxEnv } from './snapshot/lynx/env.js';
import { injectLepusMethods } from './snapshot/lynx/injectLepusMethods.js';
import { initTimingAPI } from './snapshot/lynx/performance.js';
+import { injectPrepareLazyBundleMTS } from './snapshot/lynx/prepareLazyBundleMTS.js';
import { injectTt } from './snapshot/lynx/tt.js';
import { injectUpdateMTRefInitValue } from './snapshot/worklet/ref/updateInitValue.js';
import { lynxQueueMicrotask } from './utils.js';
@@ -37,6 +38,12 @@ if (typeof __MAIN_THREAD__ !== 'undefined' && __MAIN_THREAD__) {
injectCalledByNative();
injectUpdateMainThread();
injectUpdateMTRefInitValue();
+ if (
+ typeof __LAZY_BUNDLE_FETCHER__ !== 'undefined'
+ && __LAZY_BUNDLE_FETCHER__ === 'FetchBundle'
+ ) {
+ injectPrepareLazyBundleMTS();
+ }
if (__DEV__) {
injectLepusMethods();
}
diff --git a/packages/react/runtime/src/snapshot/lifecycle/constant.ts b/packages/react/runtime/src/snapshot/lifecycle/constant.ts
index cdab91b8af..bce69a3c97 100644
--- a/packages/react/runtime/src/snapshot/lifecycle/constant.ts
+++ b/packages/react/runtime/src/snapshot/lifecycle/constant.ts
@@ -9,6 +9,7 @@ export const LifecycleConstant = {
patchUpdate: 'rLynxChange',
publishEvent: 'rLynxPublishEvent',
updateMTRefInitValue: 'rLynxChangeRefInitValue',
+ prepareLazyBundleMTS: 'rLynxPrepareLazyBundleMTS',
} as const;
export type LifecycleConstant = (typeof LifecycleConstant)[keyof typeof LifecycleConstant];
diff --git a/packages/react/runtime/src/snapshot/lynx/dynamic-js.ts b/packages/react/runtime/src/snapshot/lynx/dynamic-js.ts
index 6780953879..5a3e24ba08 100644
--- a/packages/react/runtime/src/snapshot/lynx/dynamic-js.ts
+++ b/packages/react/runtime/src/snapshot/lynx/dynamic-js.ts
@@ -1,7 +1,7 @@
// 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 { loadLazyBundle } from './lazy-bundle.js';
+import { loadLazyBundle, withLazyBundleMode } from './lazy-bundle.js';
export function loadDynamicJS(url: string): Promise {
if (__LEPUS__) {
@@ -25,10 +25,14 @@ export function loadDynamicJS(url: string): Promise {
export function __dynamicImport(
url: string,
- options?: { with?: { type?: 'component' | 'tsx' | 'jsx' } },
+ options?: { with?: { type?: 'component' | 'tsx' | 'jsx'; mode?: 'sync' | 'async' } },
): Promise {
- const t = options?.with?.type;
+ const w = options?.with;
+ const t = w?.type;
if (t === 'component' || t === 'tsx' || t === 'jsx') {
+ if (w?.mode) {
+ return withLazyBundleMode(w.mode, () => loadLazyBundle(url));
+ }
return loadLazyBundle(url);
} else {
return loadDynamicJS(url);
diff --git a/packages/react/runtime/src/snapshot/lynx/lazy-bundle.ts b/packages/react/runtime/src/snapshot/lynx/lazy-bundle.ts
index f4494e8775..2582341531 100644
--- a/packages/react/runtime/src/snapshot/lynx/lazy-bundle.ts
+++ b/packages/react/runtime/src/snapshot/lynx/lazy-bundle.ts
@@ -2,6 +2,14 @@
// 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 {
+ LYNX_LAZY_SYNC_TIMEOUT_SECONDS,
+ SECTION_BACKGROUND,
+ SECTION_CSS,
+ SECTION_MAIN_THREAD,
+} from './lazyBundleConstants.js';
+import { LifecycleConstant } from '../lifecycle/constant.js';
+
/**
* To make code below works
* const App1 = lazy(() => import("./x").then(({App1}) => ({default: App1})))
@@ -55,6 +63,8 @@ export const makeSyncThen = function(result: T): Promise['then'] {
};
};
+let lazyBundleMode: 'sync' | 'async' | undefined;
+
/**
* Load dynamic component from source. Designed to be used with `lazy`.
* @param source - where dynamic component template.js locates
@@ -64,9 +74,20 @@ export const makeSyncThen = function(result: T): Promise['then'] {
export const loadLazyBundle: <
T extends { default: React.ComponentType },
>(source: string) => Promise = /*#__PURE__*/ (() => {
- lynx.loadLazyBundle = loadLazyBundle;
+ // Default to QueryComponent when `__LAZY_BUNDLE_FETCHER__` is missing —
+ // older react-webpack-plugin builds don't stamp it and they predate
+ // FetchBundle support, so falling through to QueryComponent is the only
+ // safe behavior.
+ const useFetchBundle = typeof __LAZY_BUNDLE_FETCHER__ !== 'undefined'
+ && __LAZY_BUNDLE_FETCHER__ === 'FetchBundle';
+
+ const impl = useFetchBundle
+ ? loadLazyBundleWithFetchBundle
+ : loadLazyBundleWithQueryComponent;
- function loadLazyBundle<
+ lynx.loadLazyBundle = impl;
+
+ function loadLazyBundleWithQueryComponent<
T extends { default: React.ComponentType },
>(source: string): Promise {
if (__LEPUS__) {
@@ -89,6 +110,12 @@ export const loadLazyBundle: <
r.then = makeSyncThen(result);
return r;
} else if (__JS__) {
+ if (__DEV__ && lazyBundleMode !== undefined) {
+ throw new Error(
+ `Lazy bundle import \`mode: '${lazyBundleMode}'\` requires FetchBundle, but the current build uses QueryComponent. `
+ + `Set \`engineVersion: '3.8'\` (or higher) in \`pluginReactLynx\` to enable FetchBundle.`,
+ );
+ }
const resolver = withSyncResolvers();
const callback: (result: { code: number; detail: { schema: string } }) => void = result => {
@@ -132,7 +159,116 @@ export const loadLazyBundle: <
throw new Error('unreachable');
}
- return loadLazyBundle;
+ function loadLazyBundleWithFetchBundle<
+ T extends { default: React.ComponentType },
+ >(source: string): Promise {
+ if (__MAIN_THREAD__) {
+ if (lazyBundleMode !== 'sync') {
+ return new Promise(() => {});
+ }
+ let response;
+ try {
+ response = lynx.fetchBundle(source, {}).wait(
+ LYNX_LAZY_SYNC_TIMEOUT_SECONDS,
+ );
+ } catch {
+ return new Promise(() => {});
+ }
+ if (!response || response.code !== 0) {
+ return new Promise(() => {});
+ }
+ let result: T;
+ try {
+ result = lynx.loadScript(SECTION_MAIN_THREAD, {
+ bundleName: response.url,
+ });
+ const styleSheet = __LoadStyleSheet(SECTION_CSS, response.url);
+ if (styleSheet !== null) {
+ __AdoptStyleSheet(styleSheet);
+ }
+ } catch {
+ return new Promise(() => {});
+ }
+ const r: Promise = Promise.resolve(result);
+ r.then = makeSyncThen(result);
+ return r;
+ } else if (__JS__) {
+ if (lazyBundleMode === 'sync') {
+ let response;
+ try {
+ response = lynx.fetchBundle(source, {}).wait(
+ LYNX_LAZY_SYNC_TIMEOUT_SECONDS,
+ );
+ } catch (e) {
+ return Promise.reject(e instanceof Error ? e : new Error(String(e)));
+ }
+ if (!response || response.code !== 0) {
+ console.error('Lazy bundle load failed', response);
+ const e = new Error('Lazy bundle load failed, schema: ' + source);
+ e.cause = JSON.stringify(response);
+ return Promise.reject(e);
+ }
+ let result: T;
+ try {
+ result = lynx.loadScript(SECTION_BACKGROUND, {
+ bundleName: response.url,
+ });
+ } catch (e) {
+ return Promise.reject(e instanceof Error ? e : new Error(String(e)));
+ }
+ const r: Promise = Promise.resolve(result);
+ r.then = makeSyncThen(result);
+ return r;
+ }
+
+ // async (default)
+ return new Promise((resolve, reject) => {
+ let handler;
+ try {
+ handler = lynx.fetchBundle(source, {});
+ } catch (e) {
+ reject(e instanceof Error ? e : new Error(String(e)));
+ return;
+ }
+ handler.then((response) => {
+ if (!response || response.code !== 0) {
+ console.error('Lazy bundle load failed', response);
+ const e = new Error('Lazy bundle load failed, schema: ' + source);
+ e.cause = JSON.stringify(response);
+ reject(e);
+ return;
+ }
+ let btsResult: T;
+ try {
+ btsResult = lynx.loadScript(SECTION_BACKGROUND, {
+ bundleName: response.url,
+ });
+ } catch (e) {
+ reject(e instanceof Error ? e : new Error(String(e)));
+ return;
+ }
+ // Bundle is now in native cache, so MT's `.then` fires sync and
+ // the whole prepare runs synchronously inside `Call`, meaning the
+ // cb fires only after MT snapshots are registered.
+ try {
+ lynx.getNativeApp().callLepusMethod(
+ LifecycleConstant.prepareLazyBundleMTS,
+ { url: source },
+ () => {
+ resolve(btsResult);
+ },
+ );
+ } catch (e) {
+ reject(e instanceof Error ? e : new Error(String(e)));
+ }
+ });
+ });
+ }
+
+ throw new Error('unreachable');
+ }
+
+ return impl;
})();
function withSyncResolvers() {
@@ -156,3 +292,19 @@ function withSyncResolvers() {
return resolver;
}
+
+/**
+ * Temporarily set import mode for lazy bundle.
+ * @param mode Import mode.
+ * @param factory Factory function.
+ * @returns Result of factory function.
+ */
+export function withLazyBundleMode(mode: 'sync' | 'async', factory: () => T): T {
+ const prev = lazyBundleMode;
+ lazyBundleMode = mode;
+ try {
+ return factory();
+ } finally {
+ lazyBundleMode = prev;
+ }
+}
diff --git a/packages/react/runtime/src/snapshot/lynx/lazyBundleConstants.ts b/packages/react/runtime/src/snapshot/lynx/lazyBundleConstants.ts
new file mode 100644
index 0000000000..34ee727738
--- /dev/null
+++ b/packages/react/runtime/src/snapshot/lynx/lazyBundleConstants.ts
@@ -0,0 +1,9 @@
+// Copyright 2025 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.
+
+export const SECTION_MAIN_THREAD = 'main-thread';
+export const SECTION_BACKGROUND = 'background';
+export const SECTION_CSS = 'CSS';
+
+export const LYNX_LAZY_SYNC_TIMEOUT_SECONDS = 5;
diff --git a/packages/react/runtime/src/snapshot/lynx/prepareLazyBundleMTS.ts b/packages/react/runtime/src/snapshot/lynx/prepareLazyBundleMTS.ts
new file mode 100644
index 0000000000..763e15a98a
--- /dev/null
+++ b/packages/react/runtime/src/snapshot/lynx/prepareLazyBundleMTS.ts
@@ -0,0 +1,54 @@
+// Copyright 2025 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 { SECTION_CSS, SECTION_MAIN_THREAD } from './lazyBundleConstants.js';
+import { LifecycleConstant } from '../lifecycle/constant.js';
+
+const cache = new Set();
+
+function prepareLazyBundleMTS(payload: { url: string }): void {
+ const { url } = payload;
+ if (cache.has(url)) return;
+ cache.add(url);
+ let handler;
+ try {
+ handler = lynx.fetchBundle(url, {});
+ } catch {
+ return;
+ }
+ // .then will be a sync function
+ // since the bundle has been loaded in BTS
+ handler.then((response) => {
+ if (!response || response.code !== 0) return;
+ let loaded: unknown;
+ try {
+ loaded = lynx.loadScript(SECTION_MAIN_THREAD, {
+ bundleName: response.url,
+ });
+ } catch {
+ // BG-only bundle (no main-thread section)
+ return;
+ }
+ const processEvalResult = (
+ globalThis as unknown as {
+ processEvalResult?: (
+ result: ((schema: string) => unknown) | undefined,
+ schema: string,
+ ) => unknown;
+ }
+ ).processEvalResult;
+ if (typeof processEvalResult === 'function') {
+ processEvalResult(() => loaded, url);
+ }
+ const styleSheet = __LoadStyleSheet(SECTION_CSS, response.url);
+ if (styleSheet !== null) __AdoptStyleSheet(styleSheet);
+ });
+}
+
+/** @internal */
+export function injectPrepareLazyBundleMTS(): void {
+ Object.assign(globalThis, {
+ [LifecycleConstant.prepareLazyBundleMTS]: prepareLazyBundleMTS,
+ });
+}
diff --git a/packages/react/runtime/types/types.d.ts b/packages/react/runtime/types/types.d.ts
index 0ed0f34a3e..6dcad2e982 100644
--- a/packages/react/runtime/types/types.d.ts
+++ b/packages/react/runtime/types/types.d.ts
@@ -23,6 +23,7 @@ declare global {
declare const __ALOG_ELEMENT_API__: boolean | undefined;
declare const __ENABLE_SSR__: boolean;
declare const __GLOBAL_PROPS_MODE__: 'reactive' | 'event' | undefined;
+ declare const __LAZY_BUNDLE_FETCHER__: 'FetchBundle' | 'QueryComponent';
declare function __CreatePage(componentId: string, cssId: number): FiberElement;
declare function __CreateElement(
@@ -105,6 +106,12 @@ declare global {
element: FiberElement,
options: FlushOptions,
): void;
+ declare function __LoadStyleSheet(
+ sectionName: string,
+ bundleUrl: string,
+ ): StyleSheet | null;
+ declare function __AdoptStyleSheet(styleSheet: StyleSheet): void;
+ declare interface StyleSheet {}
declare function __UpdateListCallbacks(
list: FiberElement,
componentAtIndex: ComponentAtIndexCallback | null,
diff --git a/packages/react/transform/crates/swc_plugin_dynamic_import/Cargo.toml b/packages/react/transform/crates/swc_plugin_dynamic_import/Cargo.toml
index 7c7cfb7231..d86fc57dfc 100644
--- a/packages/react/transform/crates/swc_plugin_dynamic_import/Cargo.toml
+++ b/packages/react/transform/crates/swc_plugin_dynamic_import/Cargo.toml
@@ -9,6 +9,7 @@ path = "lib.rs"
[dependencies]
napi = { workspace = true, optional = true }
napi-derive = { workspace = true, optional = true }
+once_cell = { workspace = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["preserve_order"] }
swc_core = { workspace = true, features = ["ecma_parser", "ecma_utils", "ecma_visit", "testing_transform"] }
diff --git a/packages/react/transform/crates/swc_plugin_dynamic_import/lib.rs b/packages/react/transform/crates/swc_plugin_dynamic_import/lib.rs
index 4ef6e20cf1..d840c7d1f7 100644
--- a/packages/react/transform/crates/swc_plugin_dynamic_import/lib.rs
+++ b/packages/react/transform/crates/swc_plugin_dynamic_import/lib.rs
@@ -1,6 +1,11 @@
+use once_cell::sync::Lazy;
use serde::Deserialize;
use serde_json::Value;
-use std::{borrow::Cow, collections::HashSet, fmt::Debug};
+use std::{
+ borrow::Cow,
+ collections::{HashMap, HashSet},
+ fmt::Debug,
+};
use swc_core::{
common::{
comments::{Comment, CommentKind, Comments},
@@ -10,7 +15,7 @@ use swc_core::{
},
ecma::{
ast::*,
- utils::{calc_literal_cost, prepend_stmt},
+ utils::{calc_literal_cost, prepend_stmt, private_ident},
visit::{VisitMut, VisitMutWith},
},
};
@@ -49,6 +54,7 @@ where
has_inner_lazy_bundle: bool,
named_imports: HashSet,
comments: Option,
+ with_mode: Lazy,
}
impl Default for DynamicImportVisitor
@@ -70,6 +76,7 @@ where
comments,
has_inner_lazy_bundle: false,
named_imports: HashSet::new(),
+ with_mode: Lazy::new(|| Expr::Ident(private_ident!("withLazyBundleMode"))),
}
}
}
@@ -94,24 +101,31 @@ fn is_import_call_tpl(call_expr: &CallExpr) -> bool {
}
}
-fn is_import_call_with_type(call_expr: &CallExpr) -> (bool, bool, Value) {
+fn is_import_call_with_attrs(
+ call_expr: &CallExpr,
+ attrs: &[&str],
+) -> (bool, HashSet, HashMap) {
+ let mut with_keys = HashSet::new();
+ let mut with_values = HashMap::new();
+
match &call_expr.callee {
Callee::Import(_) if call_expr.args.len() >= 2 => match &*call_expr.args[1].expr {
Expr::Object(object) => {
let (is_lit, _) = calc_literal_cost(object, false);
if is_lit {
let with = jsonify(Expr::Object(object.clone()));
- match with.pointer("/with/type") {
- Some(value) => (true, true, value.clone()),
- _ => (true, false, Value::Null),
+ for attr in attrs.iter() {
+ if let Some(value) = with.pointer(&format!("/with/{attr}")) {
+ with_keys.insert(attr.to_string());
+ with_values.insert(attr.to_string(), value.clone());
+ }
}
- } else {
- (true, false, Value::Null)
}
+ (true, with_keys, with_values)
}
- _ => (true, false, Value::Null),
+ _ => (true, with_keys, with_values),
},
- _ => (false, false, Value::Null),
+ _ => (false, with_keys, with_values),
}
}
@@ -167,7 +181,9 @@ where
}
let (is_import_call_lit, is_import_call_str_lit, str_lit) = is_import_call_str_lit(call_expr);
- let (has_option, is_import_call_with_type, _with_type) = is_import_call_with_type(call_expr);
+ let attrs = &["type", "mode"];
+ let (has_option, with_keys, with_values) = is_import_call_with_attrs(call_expr, attrs);
+ let is_import_call_with_allow_attrs = with_keys.iter().any(|k| attrs.contains(&k.as_str()));
// TODO: reject dynamic import without `{ with: { type: "component" } }`
@@ -194,7 +210,7 @@ where
|| str_lit.starts_with("//");
if is_import_call_str_lit && !is_explicitly_external {
- if has_option && !is_import_call_with_type {
+ if has_option && !is_import_call_with_allow_attrs {
HANDLER.with(|handler| {
handler
.struct_span_err(
@@ -218,6 +234,57 @@ where
},
);
self.has_inner_lazy_bundle = true;
+
+ if with_values.contains_key("mode") {
+ let mode = with_values.get("mode").unwrap();
+ if let Value::String(mode) = mode {
+ let inner = call_expr.take();
+ let arrow = ArrowExpr {
+ span: DUMMY_SP,
+ ctxt: Default::default(),
+ params: vec![],
+ body: Box::new(BlockStmtOrExpr::Expr(Box::new(Expr::Call(inner.clone())))),
+ is_async: false,
+ is_generator: false,
+ type_params: None,
+ return_type: None,
+ };
+ *call_expr = CallExpr {
+ span: inner.span,
+ ctxt: inner.ctxt,
+ callee: Callee::Expr(Box::new(self.with_mode.clone())),
+ args: vec![
+ ExprOrSpread {
+ spread: None,
+ expr: Box::new(Expr::Lit(Lit::Str(Str {
+ span: DUMMY_SP,
+ value: mode.to_string().into(),
+ raw: None,
+ }))),
+ },
+ ExprOrSpread {
+ spread: None,
+ expr: Box::new(Expr::Arrow(arrow)),
+ },
+ ],
+ type_args: None,
+ };
+ return;
+ } else {
+ HANDLER.with(|handler| {
+ handler
+ .struct_span_err(
+ call_expr.span,
+ "`import(..., { mode: ... })` mode must be a string"
+ .to_string()
+ .as_str(),
+ )
+ .emit()
+ });
+ call_expr.visit_mut_children_with(self);
+ return;
+ }
+ }
} else {
let ident: Ident = "__dynamicImport".into();
*call_expr = CallExpr {
@@ -282,6 +349,30 @@ where
create_import_decl("data:text/javascript;charset=utf-8,import { loadLazyBundle } from \"@lynx-js/react/internal\";lynx.loadLazyBundle = loadLazyBundle;"),
);
}
+
+ if let Some(Expr::Ident(with_mode)) = Lazy::::get(&self.with_mode) {
+ prepend_stmt(
+ &mut n.body,
+ ModuleItem::ModuleDecl(ModuleDecl::Import(ImportDecl {
+ span: DUMMY_SP,
+ specifiers: vec![ImportSpecifier::Named(ImportNamedSpecifier {
+ span: DUMMY_SP,
+ local: with_mode.clone(),
+ imported: None,
+ is_type_only: false,
+ })],
+ src: Box::new(Str {
+ span: DUMMY_SP,
+ raw: None,
+ value: "@lynx-js/react/internal".into(),
+ }),
+ type_only: Default::default(),
+ // asserts: Default::default(),
+ with: Default::default(),
+ phase: ImportPhase::Evaluation,
+ })),
+ )
+ }
}
}
@@ -313,6 +404,8 @@ mod tests {
r#"
(async function () {
await import("./index.js");
+ await import("./index.js", { with: { mode: "sync" } });
+ await import("./index.js", { with: { mode: "async" } });
await import(`./locales/${name}`);
await import("ftp://www/a.js");
await import("https://www/a.js");
@@ -320,10 +413,18 @@ mod tests {
await import(url+"?v=1.0");
await import("./index.js", { with: { type: "component" } });
+ await import("./index.js", { with: { type: "component", mode: "sync" } });
+ await import("./index.js", { with: { type: "component", mode: "async" } });
await import("ftp://www/a.js", { with: { type: "component" } });
await import("https://www/a.js", { with: { type: "component" } });
+ await import("https://www/a.js", { with: { type: "component", mode: "sync" } });
+ await import("https://www/a.js", { with: { type: "component", mode: "async" } });
await import(url, { with: { type: "component" } });
+ await import(url, { with: { type: "component", mode: "sync" } });
+ await import(url, { with: { type: "component", mode: "async" } });
await import(url+"?v=1.0", { with: { type: "component" } });
+ await import(url+"?v=1.0", { with: { type: "component", mode: "sync" } });
+ await import(url+"?v=1.0", { with: { type: "component", mode: "async" } });
})();
"#
);
diff --git a/packages/react/transform/crates/swc_plugin_dynamic_import/tests/__swc_snapshots__/lib.rs/should_transform_import_call.js b/packages/react/transform/crates/swc_plugin_dynamic_import/tests/__swc_snapshots__/lib.rs/should_transform_import_call.js
index 1efb4a12e6..9e6eb45c77 100644
--- a/packages/react/transform/crates/swc_plugin_dynamic_import/tests/__swc_snapshots__/lib.rs/should_transform_import_call.js
+++ b/packages/react/transform/crates/swc_plugin_dynamic_import/tests/__swc_snapshots__/lib.rs/should_transform_import_call.js
@@ -1,7 +1,18 @@
+import { withLazyBundleMode } from "@lynx-js/react/internal";
import "@lynx-js/react/experimental/lazy/import";
import { __dynamicImport } from "@lynx-js/react/internal";
(async function() {
await import(/*webpackChunkName: "./index.js-test"*/ "./index.js");
+ await withLazyBundleMode("sync", ()=>import(/*webpackChunkName: "./index.js-test"*/ "./index.js", {
+ with: {
+ mode: "sync"
+ }
+ }));
+ await withLazyBundleMode("async", ()=>import(/*webpackChunkName: "./index.js-test"*/ "./index.js", {
+ with: {
+ mode: "async"
+ }
+ }));
await import(`./locales/${name}`);
await import(/*webpackChunkName: "ftp://www/a.js-test"*/ "ftp://www/a.js");
await __dynamicImport("https://www/a.js");
@@ -12,6 +23,18 @@ import { __dynamicImport } from "@lynx-js/react/internal";
type: "component"
}
});
+ await withLazyBundleMode("sync", ()=>import(/*webpackChunkName: "./index.js-test"*/ "./index.js", {
+ with: {
+ type: "component",
+ mode: "sync"
+ }
+ }));
+ await withLazyBundleMode("async", ()=>import(/*webpackChunkName: "./index.js-test"*/ "./index.js", {
+ with: {
+ type: "component",
+ mode: "async"
+ }
+ }));
await import(/*webpackChunkName: "ftp://www/a.js-test"*/ "ftp://www/a.js", {
with: {
type: "component"
@@ -22,14 +45,50 @@ import { __dynamicImport } from "@lynx-js/react/internal";
type: "component"
}
});
+ await __dynamicImport("https://www/a.js", {
+ with: {
+ type: "component",
+ mode: "sync"
+ }
+ });
+ await __dynamicImport("https://www/a.js", {
+ with: {
+ type: "component",
+ mode: "async"
+ }
+ });
await __dynamicImport(url, {
with: {
type: "component"
}
});
+ await __dynamicImport(url, {
+ with: {
+ type: "component",
+ mode: "sync"
+ }
+ });
+ await __dynamicImport(url, {
+ with: {
+ type: "component",
+ mode: "async"
+ }
+ });
await __dynamicImport(url + "?v=1.0", {
with: {
type: "component"
}
});
+ await __dynamicImport(url + "?v=1.0", {
+ with: {
+ type: "component",
+ mode: "sync"
+ }
+ });
+ await __dynamicImport(url + "?v=1.0", {
+ with: {
+ type: "component",
+ mode: "async"
+ }
+ });
})();
diff --git a/packages/react/types/react.docs.d.ts b/packages/react/types/react.docs.d.ts
index 51c4d5a598..edef1e42d9 100644
--- a/packages/react/types/react.docs.d.ts
+++ b/packages/react/types/react.docs.d.ts
@@ -30,6 +30,12 @@ declare global {
* Determines if running in profile mode
*/
let __PROFILE__: boolean;
+ /**
+ * Which lazy bundle fetcher the build is wired up to. `'FetchBundle'`
+ * enables the `lynx.fetchBundle`-based path (and `import(..., { with: { mode } })`
+ * mode hints); `'QueryComponent'` is the legacy `lynx.QueryComponent` path.
+ */
+ let __LAZY_BUNDLE_FETCHER__: 'FetchBundle' | 'QueryComponent';
}
/**
diff --git a/packages/repl/package.json b/packages/repl/package.json
index 36c2a90868..7928dd6899 100644
--- a/packages/repl/package.json
+++ b/packages/repl/package.json
@@ -26,7 +26,7 @@
"devDependencies": {
"@lynx-js/lynx-core": "0.1.3",
"@lynx-js/type-element-api": "0.0.3",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@lynx-js/web-core": "workspace:*",
"@lynx-js/web-platform-rsbuild-plugin": "workspace:*",
"@rsbuild/core": "catalog:rsbuild",
diff --git a/packages/rspeedy/create-rspeedy/template-react-ts/package.json b/packages/rspeedy/create-rspeedy/template-react-ts/package.json
index a26a4e6545..f331a1485b 100644
--- a/packages/rspeedy/create-rspeedy/template-react-ts/package.json
+++ b/packages/rspeedy/create-rspeedy/template-react-ts/package.json
@@ -15,7 +15,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@rsbuild/plugin-type-check": "1.3.4",
"@types/react": "^18.3.28",
"typescript": "~5.9.3"
diff --git a/packages/rspeedy/plugin-react/src/entry.ts b/packages/rspeedy/plugin-react/src/entry.ts
index 607492d328..41bb99a25b 100644
--- a/packages/rspeedy/plugin-react/src/entry.ts
+++ b/packages/rspeedy/plugin-react/src/entry.ts
@@ -21,6 +21,7 @@ import {
} from '@lynx-js/template-webpack-plugin'
import type { PluginReactLynxOptions } from './pluginReactLynx.js'
+import { resolveLazyBundleFetcher } from './resolveLazyBundleFetcher.js'
const PLUGIN_NAME_REACT = 'lynx:react'
const PLUGIN_NAME_TEMPLATE = 'lynx:template'
@@ -56,6 +57,8 @@ export function applyEntry(
experimental_isLazyBundle,
} = options
+ const lazyBundleFetcher = resolveLazyBundleFetcher(targetSdkVersion)
+
api.modifyBundlerChain(async (chain, { environment, isDev, isProd }) => {
const mainThreadChunks: string[] = []
@@ -203,6 +206,7 @@ export function applyEntry(
targetSdkVersion,
experimental_isLazyBundle,
+ lazyBundleFetcher,
cssPlugins: [],
}])
.end()
@@ -284,6 +288,7 @@ export function applyEntry(
workletRuntimePath: await resolve(
`@lynx-js/react/${isDev ? 'worklet-dev-runtime' : 'worklet-runtime'}`,
),
+ lazyBundleFetcher,
}])
function getDefaultProfile(): boolean | undefined {
diff --git a/packages/rspeedy/plugin-react/src/resolveLazyBundleFetcher.ts b/packages/rspeedy/plugin-react/src/resolveLazyBundleFetcher.ts
new file mode 100644
index 0000000000..3eed3720dc
--- /dev/null
+++ b/packages/rspeedy/plugin-react/src/resolveLazyBundleFetcher.ts
@@ -0,0 +1,49 @@
+// 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.
+
+const FETCH_BUNDLE_MIN_ENGINE_VERSION = '3.8'
+
+export function resolveLazyBundleFetcher(
+ engineVersion: string | undefined,
+): 'FetchBundle' | 'QueryComponent' {
+ const meets = meetsMinEngineVersion(
+ engineVersion,
+ FETCH_BUNDLE_MIN_ENGINE_VERSION,
+ )
+ const envOverride = process.env['REACT_LAZY_BUNDLE_FETCHER']
+ if (envOverride === 'FetchBundle' && !meets) {
+ throw new Error(
+ `[pluginReactLynx] REACT_LAZY_BUNDLE_FETCHER=FetchBundle `
+ + `requires engineVersion >= ${FETCH_BUNDLE_MIN_ENGINE_VERSION}, `
+ + `but got ${engineVersion ? `'${engineVersion}'` : ''}. `
+ + `Older hosts do not expose 'lynx.fetchBundle' / 'lynx.loadScript'. `
+ + `Either bump 'engineVersion' to `
+ + `'${FETCH_BUNDLE_MIN_ENGINE_VERSION}' or higher, or unset `
+ + `REACT_LAZY_BUNDLE_FETCHER (the default falls back to `
+ + `'QueryComponent' on older hosts).`,
+ )
+ }
+ if (envOverride === 'FetchBundle' || envOverride === 'QueryComponent') {
+ return envOverride
+ }
+ return meets ? 'FetchBundle' : 'QueryComponent'
+}
+
+function meetsMinEngineVersion(
+ actual: string | undefined,
+ min: string,
+): boolean {
+ if (!actual) return false
+ const actualParts = actual.split('.').map(Number)
+ const minParts = min.split('.').map(Number)
+ const len = Math.max(actualParts.length, minParts.length)
+ for (let i = 0; i < len; i++) {
+ const a = actualParts[i] ?? 0
+ const m = minParts[i] ?? 0
+ if (Number.isNaN(a) || Number.isNaN(m)) return false
+ if (a > m) return true
+ if (a < m) return false
+ }
+ return true
+}
diff --git a/packages/rspeedy/plugin-react/test/config.test.ts b/packages/rspeedy/plugin-react/test/config.test.ts
index 0376185045..d8524de016 100644
--- a/packages/rspeedy/plugin-react/test/config.test.ts
+++ b/packages/rspeedy/plugin-react/test/config.test.ts
@@ -2245,6 +2245,7 @@ describe('Config', () => {
"experimental_isLazyBundle": false,
"filename": "main.lynx.bundle",
"intermediate": ".rspeedy/main",
+ "lazyBundleFetcher": "QueryComponent",
"removeDescendantSelectorScope": true,
"targetSdkVersion": "3.2",
}
diff --git a/packages/rspeedy/plugin-react/test/resolveLazyBundleFetcher.test.ts b/packages/rspeedy/plugin-react/test/resolveLazyBundleFetcher.test.ts
new file mode 100644
index 0000000000..01933cdca2
--- /dev/null
+++ b/packages/rspeedy/plugin-react/test/resolveLazyBundleFetcher.test.ts
@@ -0,0 +1,95 @@
+// 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, beforeEach, describe, expect, test } from 'vitest'
+
+import { resolveLazyBundleFetcher } from '../src/resolveLazyBundleFetcher.js'
+
+describe('resolveLazyBundleFetcher', () => {
+ const originalEnv = process.env['REACT_LAZY_BUNDLE_FETCHER']
+ beforeEach(() => {
+ delete process.env['REACT_LAZY_BUNDLE_FETCHER']
+ })
+ afterEach(() => {
+ if (originalEnv === undefined) {
+ delete process.env['REACT_LAZY_BUNDLE_FETCHER']
+ } else {
+ process.env['REACT_LAZY_BUNDLE_FETCHER'] = originalEnv
+ }
+ })
+
+ describe('default behavior (no env override)', () => {
+ test('undefined engineVersion → QueryComponent', () => {
+ expect(resolveLazyBundleFetcher(undefined)).toBe('QueryComponent')
+ })
+
+ test('engineVersion below 3.8 → QueryComponent', () => {
+ expect(resolveLazyBundleFetcher('3.7')).toBe('QueryComponent')
+ expect(resolveLazyBundleFetcher('3.7.9')).toBe('QueryComponent')
+ expect(resolveLazyBundleFetcher('2.16')).toBe('QueryComponent')
+ expect(resolveLazyBundleFetcher('3.0')).toBe('QueryComponent')
+ })
+
+ test('engineVersion at or above 3.8 → FetchBundle', () => {
+ expect(resolveLazyBundleFetcher('3.8')).toBe('FetchBundle')
+ expect(resolveLazyBundleFetcher('3.8.0')).toBe('FetchBundle')
+ expect(resolveLazyBundleFetcher('3.8.1')).toBe('FetchBundle')
+ expect(resolveLazyBundleFetcher('3.9')).toBe('FetchBundle')
+ expect(resolveLazyBundleFetcher('4.0')).toBe('FetchBundle')
+ })
+
+ test('multi-digit minor compares numerically (3.10 > 3.8)', () => {
+ expect(resolveLazyBundleFetcher('3.10')).toBe('FetchBundle')
+ expect(resolveLazyBundleFetcher('3.10.5')).toBe('FetchBundle')
+ })
+
+ test('non-numeric version → QueryComponent (NaN guard)', () => {
+ expect(resolveLazyBundleFetcher('foo')).toBe('QueryComponent')
+ expect(resolveLazyBundleFetcher('3.x')).toBe('QueryComponent')
+ expect(resolveLazyBundleFetcher('latest')).toBe('QueryComponent')
+ })
+ })
+
+ describe('REACT_LAZY_BUNDLE_FETCHER env override', () => {
+ test('=FetchBundle with sufficient version → FetchBundle', () => {
+ process.env['REACT_LAZY_BUNDLE_FETCHER'] = 'FetchBundle'
+ expect(resolveLazyBundleFetcher('3.8')).toBe('FetchBundle')
+ expect(resolveLazyBundleFetcher('4.0')).toBe('FetchBundle')
+ })
+
+ test('=FetchBundle with insufficient version → throws', () => {
+ process.env['REACT_LAZY_BUNDLE_FETCHER'] = 'FetchBundle'
+ expect(() => resolveLazyBundleFetcher('3.7')).toThrow(
+ /requires engineVersion >= 3\.8/,
+ )
+ })
+
+ test('=FetchBundle with undefined version → throws (mentions )', () => {
+ process.env['REACT_LAZY_BUNDLE_FETCHER'] = 'FetchBundle'
+ expect(() => resolveLazyBundleFetcher(undefined)).toThrow(//)
+ })
+
+ test('=QueryComponent forces legacy path even on 3.8+', () => {
+ process.env['REACT_LAZY_BUNDLE_FETCHER'] = 'QueryComponent'
+ expect(resolveLazyBundleFetcher('3.8')).toBe('QueryComponent')
+ expect(resolveLazyBundleFetcher('4.0')).toBe('QueryComponent')
+ })
+
+ test('=QueryComponent with insufficient version → QueryComponent', () => {
+ process.env['REACT_LAZY_BUNDLE_FETCHER'] = 'QueryComponent'
+ expect(resolveLazyBundleFetcher('3.7')).toBe('QueryComponent')
+ })
+
+ test('unrecognized override value falls through to default', () => {
+ process.env['REACT_LAZY_BUNDLE_FETCHER'] = 'something-else'
+ expect(resolveLazyBundleFetcher('3.8')).toBe('FetchBundle')
+ expect(resolveLazyBundleFetcher('3.7')).toBe('QueryComponent')
+ })
+
+ test('empty string override → falls through to default', () => {
+ process.env['REACT_LAZY_BUNDLE_FETCHER'] = ''
+ expect(resolveLazyBundleFetcher('3.8')).toBe('FetchBundle')
+ })
+ })
+})
diff --git a/packages/rspeedy/upgrade-rspeedy/package.json b/packages/rspeedy/upgrade-rspeedy/package.json
index bc64d1c48a..6c2eaec493 100644
--- a/packages/rspeedy/upgrade-rspeedy/package.json
+++ b/packages/rspeedy/upgrade-rspeedy/package.json
@@ -38,7 +38,7 @@
"@lynx-js/react": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@lynx-js/web-core": "workspace:*",
"@lynx-js/web-elements": "workspace:*",
"@rsbuild/plugin-less": "1.6.0",
diff --git a/packages/testing-library/examples/react-compiler/package.json b/packages/testing-library/examples/react-compiler/package.json
index 7824f3f319..56b2a2bd0d 100644
--- a/packages/testing-library/examples/react-compiler/package.json
+++ b/packages/testing-library/examples/react-compiler/package.json
@@ -22,7 +22,7 @@
"@lynx-js/qrcode-rsbuild-plugin": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@rsbuild/plugin-babel": "1.1.0",
"@testing-library/jest-dom": "^6.9.1",
"@types/react": "^18.3.28",
diff --git a/packages/testing-library/kitten-lynx/package.json b/packages/testing-library/kitten-lynx/package.json
index 497dfb7730..a8612e85b7 100644
--- a/packages/testing-library/kitten-lynx/package.json
+++ b/packages/testing-library/kitten-lynx/package.json
@@ -45,7 +45,7 @@
"@lynx-js/react": "workspace:*",
"@lynx-js/react-rsbuild-plugin": "workspace:*",
"@lynx-js/rspeedy": "workspace:*",
- "@lynx-js/types": "3.7.0",
+ "@lynx-js/types": "3.10.2-alpha.0",
"@types/react": "^18.3.28",
"execa": "^9.6.1",
"jpeg-js": "^0.4.4",
diff --git a/packages/webpack/css-extract-webpack-plugin/package.json b/packages/webpack/css-extract-webpack-plugin/package.json
index 1bcd623b3d..9152f68774 100644
--- a/packages/webpack/css-extract-webpack-plugin/package.json
+++ b/packages/webpack/css-extract-webpack-plugin/package.json
@@ -59,7 +59,7 @@
"webpack": "^5.105.2"
},
"peerDependencies": {
- "@lynx-js/template-webpack-plugin": "^0.11.0"
+ "@lynx-js/template-webpack-plugin": "^0.11.0 || ^0.12.0"
},
"engines": {
"node": ">=18"
diff --git a/packages/webpack/react-refresh-webpack-plugin/package.json b/packages/webpack/react-refresh-webpack-plugin/package.json
index af1dbd32aa..a85827fa8d 100644
--- a/packages/webpack/react-refresh-webpack-plugin/package.json
+++ b/packages/webpack/react-refresh-webpack-plugin/package.json
@@ -49,7 +49,7 @@
"webpack": "^5.105.2"
},
"peerDependencies": {
- "@lynx-js/react-webpack-plugin": "^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 || ^0.9.0"
+ "@lynx-js/react-webpack-plugin": "^0.3.0 || ^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 || ^0.9.0 || ^0.10.0"
},
"engines": {
"node": ">=18"
diff --git a/packages/webpack/react-webpack-plugin/etc/react-webpack-plugin.api.md b/packages/webpack/react-webpack-plugin/etc/react-webpack-plugin.api.md
index ec1ef3a372..0ad334a5b2 100644
--- a/packages/webpack/react-webpack-plugin/etc/react-webpack-plugin.api.md
+++ b/packages/webpack/react-webpack-plugin/etc/react-webpack-plugin.api.md
@@ -53,6 +53,7 @@ export interface ReactWebpackPluginOptions {
extractStr?: Partial | boolean;
firstScreenSyncTiming?: 'immediately' | 'jsReady';
globalPropsMode?: 'reactive' | 'event';
+ lazyBundleFetcher?: 'FetchBundle' | 'QueryComponent';
mainThreadChunks?: string[] | undefined;
profile?: boolean | undefined;
workletRuntimePath: string;
diff --git a/packages/webpack/react-webpack-plugin/package.json b/packages/webpack/react-webpack-plugin/package.json
index 4f7a6acfc8..eba7e42ecc 100644
--- a/packages/webpack/react-webpack-plugin/package.json
+++ b/packages/webpack/react-webpack-plugin/package.json
@@ -53,7 +53,7 @@
"webpack": "^5.105.2"
},
"peerDependencies": {
- "@lynx-js/template-webpack-plugin": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0"
+ "@lynx-js/template-webpack-plugin": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0 || ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0"
},
"peerDependenciesMeta": {
"@lynx-js/react": {
diff --git a/packages/webpack/react-webpack-plugin/src/ReactWebpackPlugin.ts b/packages/webpack/react-webpack-plugin/src/ReactWebpackPlugin.ts
index 3c3dc8b6b1..7a7fd4e25e 100644
--- a/packages/webpack/react-webpack-plugin/src/ReactWebpackPlugin.ts
+++ b/packages/webpack/react-webpack-plugin/src/ReactWebpackPlugin.ts
@@ -111,6 +111,15 @@ interface ReactWebpackPluginOptions {
* @experimental
*/
experimental_useElementTemplate?: boolean;
+
+ /**
+ * Resolved lazy-bundle fetcher mode. Decided by the caller (e.g.
+ * `pluginReactLynx`) from the host engine version and any
+ * `REACT_LAZY_BUNDLE_FETCHER` env override.
+ *
+ * @public
+ */
+ lazyBundleFetcher?: 'FetchBundle' | 'QueryComponent';
}
/**
@@ -188,6 +197,7 @@ class ReactWebpackPlugin {
profile: undefined,
workletRuntimePath: '',
experimental_useElementTemplate: false,
+ lazyBundleFetcher: 'QueryComponent',
});
/**
@@ -248,6 +258,7 @@ class ReactWebpackPlugin {
__USE_ELEMENT_TEMPLATE__: JSON.stringify(
options.experimental_useElementTemplate,
),
+ __LAZY_BUNDLE_FETCHER__: JSON.stringify(options.lazyBundleFetcher),
}).apply(compiler);
compiler.hooks.thisCompilation.tap(this.constructor.name, compilation => {
@@ -387,15 +398,21 @@ class ReactWebpackPlugin {
continue;
}
+ const isFetchBundle =
+ options.lazyBundleFetcher === 'FetchBundle';
compilation.updateAsset(
file,
old =>
new ConcatSource(
- `(function (globDynamicComponentEntry) {\n`,
+ isFetchBundle
+ ? `(function () {\n var globDynamicComponentEntry = '__Card__';\n`
+ : `(function (globDynamicComponentEntry) {\n`,
` const module = { exports: {} }\n`,
` const exports = module.exports;\n`,
old,
- `\n ;return module.exports\n})`,
+ isFetchBundle
+ ? `\n ;return module.exports\n})()`
+ : `\n ;return module.exports\n})`,
),
);
}
diff --git a/packages/webpack/react-webpack-plugin/test/fixtures/lazy-bundle-fetcher/index.jsx b/packages/webpack/react-webpack-plugin/test/fixtures/lazy-bundle-fetcher/index.jsx
new file mode 100644
index 0000000000..541bcfe79e
--- /dev/null
+++ b/packages/webpack/react-webpack-plugin/test/fixtures/lazy-bundle-fetcher/index.jsx
@@ -0,0 +1,11 @@
+// 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.
+
+// Reference the build-time define so DefinePlugin inlines it into the
+// emitted bundle, where the test can grep for the resolved literal.
+globalThis.__lynx_fetcher_probe__ = __LAZY_BUNDLE_FETCHER__;
+
+export default function App() {
+ return null;
+}
diff --git a/packages/webpack/react-webpack-plugin/test/lazy-bundle-fetcher.test.ts b/packages/webpack/react-webpack-plugin/test/lazy-bundle-fetcher.test.ts
new file mode 100644
index 0000000000..336cc995f4
--- /dev/null
+++ b/packages/webpack/react-webpack-plugin/test/lazy-bundle-fetcher.test.ts
@@ -0,0 +1,130 @@
+// 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 { mkdtemp, readFile } from 'node:fs/promises';
+import { tmpdir } from 'node:os';
+import { dirname, join } from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+import { rspack } from '@rspack/core';
+import type { Configuration, Stats } from '@rspack/core';
+import { describe, expect, test } from 'vitest';
+
+import {
+ LynxEncodePlugin,
+ LynxTemplatePlugin,
+} from '@lynx-js/template-webpack-plugin';
+
+// `create-react-config.js` is plain JS without a generated d.ts.
+// @ts-expect-error untyped JS helper
+import { createConfig as createConfigUntyped } from './create-react-config.js';
+
+const createConfig = createConfigUntyped as (
+ loaderOptions: Record,
+ pluginOptions: Record,
+) => Configuration;
+
+const __dirname = dirname(fileURLToPath(import.meta.url));
+const FIXTURE = join(__dirname, 'fixtures/lazy-bundle-fetcher/index.jsx');
+
+interface BuildResult {
+ mainThread: string;
+ background: string;
+}
+
+async function build(
+ pluginOptions: Record,
+): Promise {
+ const dist = await mkdtemp(join(tmpdir(), 'rwp-fetchbundle-'));
+ const config = createConfig({}, {
+ experimental_isLazyBundle: true,
+ mainThreadChunks: ['main__main-thread.js'],
+ ...pluginOptions,
+ });
+ config.entry = {
+ 'main__main-thread': { import: FIXTURE, layer: 'react:main-thread' },
+ 'main__background': { import: FIXTURE, layer: 'react:background' },
+ };
+ config.context = dirname(FIXTURE);
+ config.output = { ...config.output, filename: '[name].js', path: dist };
+ config.mode = 'development';
+ config.devtool = false;
+ // The wrapper-injection branch is gated on a LynxTemplatePlugin being
+ // present; add a minimal one so the gate passes.
+ config.plugins = [
+ ...(config.plugins ?? []),
+ new LynxEncodePlugin(),
+ new LynxTemplatePlugin({
+ ...LynxTemplatePlugin.defaultOptions,
+ chunks: ['main__main-thread', 'main__background'],
+ filename: 'main/template.js',
+ intermediate: '.rspeedy/main',
+ experimental_isLazyBundle: true,
+ }),
+ ];
+
+ const compiler = rspack(config);
+ let stats: Stats;
+ try {
+ stats = await new Promise((resolve, reject) => {
+ compiler.run((err, s) => {
+ if (err) return reject(err);
+ if (!s) return reject(new Error('rspack returned empty stats'));
+ resolve(s);
+ });
+ });
+ } finally {
+ await new Promise((r) => compiler.close(() => r()));
+ }
+ if (stats.hasErrors()) {
+ throw new Error(stats.toString({ all: false, errors: true }));
+ }
+
+ return {
+ mainThread: await readFile(join(dist, 'main__main-thread.js'), 'utf8'),
+ background: await readFile(
+ join(dist, 'main__background.js'),
+ 'utf8',
+ ),
+ };
+}
+
+describe('ReactWebpackPlugin: lazyBundleFetcher', () => {
+ test('FetchBundle: main-thread chunk wrapped as self-invoking IIFE with __Card__', async () => {
+ const { mainThread } = await build({ lazyBundleFetcher: 'FetchBundle' });
+ expect(mainThread).toContain(
+ `var globDynamicComponentEntry = '__Card__'`,
+ );
+ expect(mainThread.trimStart().startsWith('(function () {')).toBe(true);
+ expect(mainThread.trimEnd().endsWith('})()')).toBe(true);
+ });
+
+ test('QueryComponent (default): main-thread chunk wrapped as parameterised non-IIFE', async () => {
+ const { mainThread } = await build({});
+ expect(mainThread).not.toContain(
+ `var globDynamicComponentEntry = '__Card__'`,
+ );
+ expect(
+ mainThread.trimStart().startsWith(
+ '(function (globDynamicComponentEntry) {',
+ ),
+ ).toBe(true);
+ expect(mainThread.trimEnd().endsWith('})')).toBe(true);
+ // Importantly: NOT self-invoking.
+ expect(mainThread.trimEnd().endsWith('})()')).toBe(false);
+ });
+
+ describe('__LAZY_BUNDLE_FETCHER__ define injection', () => {
+ test('FetchBundle: define replaces references with literal "FetchBundle"', async () => {
+ const { background } = await build({ lazyBundleFetcher: 'FetchBundle' });
+ expect(background).toContain('"FetchBundle"');
+ expect(background).not.toContain('__LAZY_BUNDLE_FETCHER__');
+ });
+
+ test('QueryComponent (default): define replaces references with literal "QueryComponent"', async () => {
+ const { background } = await build({});
+ expect(background).toContain('"QueryComponent"');
+ expect(background).not.toContain('__LAZY_BUNDLE_FETCHER__');
+ });
+ });
+});
diff --git a/packages/webpack/template-webpack-plugin/etc/template-webpack-plugin.api.md b/packages/webpack/template-webpack-plugin/etc/template-webpack-plugin.api.md
index 69b3445172..f900c34b77 100644
--- a/packages/webpack/template-webpack-plugin/etc/template-webpack-plugin.api.md
+++ b/packages/webpack/template-webpack-plugin/etc/template-webpack-plugin.api.md
@@ -32,7 +32,8 @@ export interface EncodeOptions {
// (undocumented)
customSections: Record;
+ encoding?: 'JsBytecode' | 'CSS';
+ content: string | Record | undefined;
}>;
elementTemplate?: Record;
// (undocumented)
@@ -40,9 +41,9 @@ export interface EncodeOptions {
root: string | undefined;
lepusChunk: Record;
filename: string | undefined;
- };
+ } | undefined;
// (undocumented)
- manifest: Record;
+ manifest?: Record | undefined;
}
// @public
@@ -115,6 +116,7 @@ export interface LynxTemplatePluginOptions {
experimental_isLazyBundle?: boolean;
filename?: string | ((entryName: string) => string);
intermediate?: string;
+ lazyBundleFetcher?: 'FetchBundle' | 'QueryComponent';
lazyBundleFilename?: string;
removeDescendantSelectorScope: boolean;
targetSdkVersion: string;
@@ -210,6 +212,6 @@ export class WebEncodePlugin {
// Warnings were encountered during analysis:
//
-// lib/LynxTemplatePlugin.d.ts:72:9 - (ae-forgotten-export) The symbol "EncodeRawData" needs to be exported by the entry point index.d.ts
+// lib/LynxTemplatePlugin.d.ts:73:9 - (ae-forgotten-export) The symbol "EncodeRawData" needs to be exported by the entry point index.d.ts
```
diff --git a/packages/webpack/template-webpack-plugin/rstest.config.ts b/packages/webpack/template-webpack-plugin/rstest.config.ts
index 2dbca041b3..f4186822e0 100644
--- a/packages/webpack/template-webpack-plugin/rstest.config.ts
+++ b/packages/webpack/template-webpack-plugin/rstest.config.ts
@@ -17,6 +17,7 @@ const config: Parameters[0] = {
],
env: {
DEBUG: 'rspeedy',
+ REACT_LAZY_BUNDLE_FETCHER: 'QueryComponent',
},
};
diff --git a/packages/webpack/template-webpack-plugin/src/LynxTemplatePlugin.ts b/packages/webpack/template-webpack-plugin/src/LynxTemplatePlugin.ts
index fa8dcf9df3..e2e5a8eaee 100644
--- a/packages/webpack/template-webpack-plugin/src/LynxTemplatePlugin.ts
+++ b/packages/webpack/template-webpack-plugin/src/LynxTemplatePlugin.ts
@@ -37,17 +37,18 @@ export type OriginManifest = Record;
+ manifest?: Record | undefined;
compilerOptions: Record;
lepusCode: {
root: string | undefined;
lepusChunk: Record;
filename: string | undefined;
- };
+ } | undefined;
// `customSections` option only takes effect on Lynx >= 2.16.
customSections: Record;
+ encoding?: 'JsBytecode' | 'CSS';
+ content: string | Record | undefined;
}>;
/**
* Element template data used by encoders that support element template output.
@@ -299,6 +300,15 @@ export interface LynxTemplatePluginOptions {
*/
experimental_isLazyBundle?: boolean;
+ /**
+ * Resolved lazy-bundle fetcher mode. Decided by the caller (e.g.
+ * `pluginReactLynx`) from the host engine version and any
+ * `REACT_LAZY_BUNDLE_FETCHER` env override.
+ *
+ * @public
+ */
+ lazyBundleFetcher?: 'FetchBundle' | 'QueryComponent';
+
/**
* plugins passed to parser
*/
@@ -329,6 +339,7 @@ interface EncodeRawData {
// `customSections` option only takes effect on Lynx >= 2.16.
customSections: Record;
}>;
sourceContent: {
@@ -409,6 +420,7 @@ export class LynxTemplatePlugin {
dsl: 'react_nodiff',
experimental_isLazyBundle: false,
+ lazyBundleFetcher: 'QueryComponent',
cssPlugins: [],
});
@@ -482,6 +494,16 @@ interface Hash {
digest(encoding?: string): string | Buffer;
}
+const SECTION_MAIN_THREAD = 'main-thread';
+const SECTION_BACKGROUND = 'background';
+const SECTION_CSS = 'CSS';
+
+interface CustomSectionEntry {
+ type?: 'lazy';
+ encoding?: 'JsBytecode' | 'CSS';
+ content: string | Record;
+}
+
class LynxTemplatePluginImpl {
name = 'LynxTemplatePlugin';
@@ -872,23 +894,49 @@ class LynxTemplatePluginImpl {
const { lepusCode, css } = encodeData;
+ const lepusChunk = Object.fromEntries(
+ lepusCode.chunks.map(asset => {
+ return [asset.name, asset.source.source().toString()];
+ }),
+ );
+
+ const isFetchBundleLazy = isAsync
+ && this.#options.lazyBundleFetcher === 'FetchBundle';
+ // Default to bytecode for FetchBundle lazy main-thread sections. Skip
+ // in dev or when DEBUG matches rspeedy so the source stays debuggable.
+ const enableLazyBundleBytecode = isFetchBundleLazy && !isDev
+ && !isDebug();
+ const fetchBundleSplit = isFetchBundleLazy
+ ? this.#buildLazyBundleFetchBundleSections(
+ lepusCode.root,
+ encodeData.manifest,
+ encodeData.css.chunks,
+ enableLazyBundleBytecode,
+ )
+ : null;
+
const resolvedEncodeOptions: EncodeOptions = {
...encodeData,
css: {
...css,
+ cssMap: fetchBundleSplit ? {} : css.cssMap,
+ cssSource: fetchBundleSplit ? {} : css.cssSource,
chunks: undefined,
contentMap: undefined,
},
- lepusCode: {
+ lepusCode: fetchBundleSplit ? undefined : {
// TODO: support multiple lepus chunks
root: lepusCode.root?.source.source().toString(),
- lepusChunk: Object.fromEntries(
- lepusCode.chunks.map(asset => {
- return [asset.name, asset.source.source().toString()];
- }),
- ),
+ lepusChunk,
filename: lepusCode.filename,
},
+ manifest: fetchBundleSplit
+ ? fetchBundleSplit.remainingManifest
+ : encodeData.manifest,
+ customSections: {
+ ...encodeData.customSections,
+ ...(fetchBundleSplit ? fetchBundleSplit.sections : {}),
+ },
};
const { RawSource } = compiler.webpack.sources;
@@ -903,7 +951,7 @@ class LynxTemplatePluginImpl {
JSON.stringify(resolvedEncodeOptions, null, 2),
),
);
- Object.entries(resolvedEncodeOptions.lepusCode.lepusChunk).forEach(
+ Object.entries(lepusChunk).forEach(
([name, content]) => {
compilation.emitAsset(
path.posix.format({
@@ -961,6 +1009,58 @@ class LynxTemplatePluginImpl {
}
}
+ #buildLazyBundleFetchBundleSections(
+ mainThreadAsset: Asset | undefined,
+ manifest: Record,
+ cssAssets: Asset[],
+ enableBytecode: boolean,
+ ): {
+ sections: Record;
+ remainingManifest: Record;
+ } {
+ const { cssPlugins, enableCSSSelector } = this.#options;
+ const sections: Record = {};
+
+ if (mainThreadAsset) {
+ sections[SECTION_MAIN_THREAD] = {
+ ...(enableBytecode ? { encoding: 'JsBytecode' as const } : {}),
+ content: mainThreadAsset.source.source().toString(),
+ };
+ }
+
+ const remainingManifest: Record = {};
+ let entryChunk: [string, string] | undefined;
+ for (const [name, content] of Object.entries(manifest)) {
+ if (name === '/app-service.js') {
+ continue;
+ }
+ if (!entryChunk) {
+ entryChunk = [name, content];
+ continue;
+ }
+ remainingManifest[name] = content;
+ }
+
+ if (entryChunk) {
+ sections[SECTION_BACKGROUND] = { content: entryChunk[1] };
+ }
+
+ const firstCss = cssAssets[0];
+ if (firstCss) {
+ const ruleList = cssChunksToMap(
+ [firstCss.source.source().toString()],
+ cssPlugins,
+ enableCSSSelector,
+ ).cssMap[0] ?? [];
+ sections[SECTION_CSS] = {
+ encoding: 'CSS',
+ content: { ruleList },
+ };
+ }
+
+ return { sections, remainingManifest };
+ }
+
/**
* Return all chunks from the compilation result which match the exclude and include filters
*/
diff --git a/packages/webpack/template-webpack-plugin/src/WebEncodePlugin.ts b/packages/webpack/template-webpack-plugin/src/WebEncodePlugin.ts
index 8462ba967d..4c8dbf8c5a 100644
--- a/packages/webpack/template-webpack-plugin/src/WebEncodePlugin.ts
+++ b/packages/webpack/template-webpack-plugin/src/WebEncodePlugin.ts
@@ -95,11 +95,13 @@ export class WebEncodePlugin {
cardType: encodeOptions['cardType'] as string,
appType: encodeOptions['appType'] as string,
pageConfig: encodeOptions['pageConfig'] as Record,
- lepusCode: {
- // flatten the lepusCode to a single object
- ...encodeOptions.lepusCode.lepusChunk,
- root: encodeOptions.lepusCode.root!,
- },
+ lepusCode: encodeOptions.lepusCode
+ ? {
+ // flatten the lepusCode to a single object
+ ...encodeOptions.lepusCode.lepusChunk,
+ root: encodeOptions.lepusCode.root!,
+ }
+ : {},
customSections: encodeOptions.customSections ?? {},
};
if (encodeOptions.elementTemplate !== undefined) {
diff --git a/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/foo.bts.js b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/foo.bts.js
new file mode 100644
index 0000000000..5f076d2fec
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/foo.bts.js
@@ -0,0 +1,4 @@
+// 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.
+export const layer = 'background';
diff --git a/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/foo.mts.js b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/foo.mts.js
new file mode 100644
index 0000000000..b340bd7651
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/foo.mts.js
@@ -0,0 +1,4 @@
+// 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.
+export const layer = 'main-thread';
diff --git a/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/index.js b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/index.js
new file mode 100644
index 0000000000..9877af8fe1
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/index.js
@@ -0,0 +1,30 @@
+/*
+// 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 { existsSync } from 'node:fs';
+import { readFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+
+void import(/* webpackChunkName: 'foo:main-thread' */ './foo.mts.js');
+void import(/* webpackChunkName: 'foo:background' */ './foo.bts.js');
+
+it('FetchBundle: lazy bundle tasm.json carries customSections shape', async () => {
+ const tasmJSONPath = resolve(__dirname, '.rspeedy/async/foo/tasm.json');
+ expect(existsSync(tasmJSONPath)).toBeTruthy();
+
+ const tasm = JSON.parse(await readFile(tasmJSONPath, 'utf-8'));
+ // FetchBundle moves MT/BG/CSS into customSections and drops the legacy
+ // lepusCode + manifest slots for lazy bundles.
+ expect(tasm.customSections).toHaveProperty('main-thread');
+ expect(tasm.customSections).toHaveProperty('background');
+ expect(tasm.customSections['main-thread'].content).toEqual(
+ expect.any(String),
+ );
+ expect(tasm.customSections['background'].content).toEqual(expect.any(String));
+ expect(tasm.lepusCode).toBeUndefined();
+ expect(tasm.css.cssMap).toEqual({});
+});
diff --git a/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/rspack.config.js b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/rspack.config.js
new file mode 100644
index 0000000000..e53c8d70f2
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/fetchbundle/rspack.config.js
@@ -0,0 +1,62 @@
+// 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 { LynxEncodePlugin, LynxTemplatePlugin } from '../../../../lib/index.js';
+
+/** @type {import('@rspack/core').Configuration} */
+export default {
+ devtool: false,
+ mode: 'development',
+ plugins: [
+ new LynxEncodePlugin(),
+ new LynxTemplatePlugin({
+ ...LynxTemplatePlugin.defaultOptions,
+ intermediate: '.rspeedy/main',
+ lazyBundleFetcher: 'FetchBundle',
+ }),
+ /**
+ * Strip the React-style layer suffixes so MT/BG chunks share a template
+ * (mirrors `react-webpack-plugin`'s `asyncChunkName` hook).
+ * @param {import('@rspack/core').Compiler} compiler
+ */
+ (compiler) => {
+ compiler.hooks.thisCompilation.tap('strip', (compilation) => {
+ const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(
+ compilation,
+ );
+ hooks.asyncChunkName.tap(
+ 'strip',
+ (chunkName) =>
+ chunkName.replace(':main-thread', '').replace(':background', ''),
+ );
+ });
+ },
+ /**
+ * Mark assets whose chunk name contains `:main-thread` as
+ * `lynx:main-thread` so LynxTemplatePlugin routes them into the
+ * mainThread bucket (mirrors `react-webpack-plugin`'s loader).
+ * @param {import('@rspack/core').Compiler} compiler
+ */
+ (compiler) => {
+ compiler.hooks.thisCompilation.tap('mark-mt', (compilation) => {
+ compilation.hooks.processAssets.tap(
+ {
+ name: 'mark-mt',
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,
+ },
+ (assets) => {
+ for (const name of Object.keys(assets)) {
+ if (!name.includes(':main-thread')) continue;
+ const asset = compilation.getAsset(name);
+ if (!asset) continue;
+ compilation.updateAsset(asset.name, asset.source, {
+ ...asset.info,
+ 'lynx:main-thread': true,
+ });
+ }
+ },
+ );
+ });
+ },
+ ],
+};
diff --git a/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/foo.bts.js b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/foo.bts.js
new file mode 100644
index 0000000000..5f076d2fec
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/foo.bts.js
@@ -0,0 +1,4 @@
+// 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.
+export const layer = 'background';
diff --git a/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/foo.mts.js b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/foo.mts.js
new file mode 100644
index 0000000000..b340bd7651
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/foo.mts.js
@@ -0,0 +1,4 @@
+// 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.
+export const layer = 'main-thread';
diff --git a/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/index.js b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/index.js
new file mode 100644
index 0000000000..a83c41c3b0
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/index.js
@@ -0,0 +1,25 @@
+/*
+// 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 { existsSync } from 'node:fs';
+import { readFile } from 'node:fs/promises';
+import { resolve } from 'node:path';
+
+void import(/* webpackChunkName: 'foo:main-thread' */ './foo.mts.js');
+void import(/* webpackChunkName: 'foo:background' */ './foo.bts.js');
+
+it('QueryComponent (default): lazy bundle keeps lepusCode + manifest shape', async () => {
+ const tasmJSONPath = resolve(__dirname, '.rspeedy/async/foo/tasm.json');
+ expect(existsSync(tasmJSONPath)).toBeTruthy();
+
+ const tasm = JSON.parse(await readFile(tasmJSONPath, 'utf-8'));
+ // Default fetcher path: customSections is empty (legacy slots own MT/BG).
+ expect(tasm.customSections['main-thread']).toBeUndefined();
+ expect(tasm.customSections['background']).toBeUndefined();
+ expect(tasm.lepusCode).toBeDefined();
+ expect(tasm.lepusCode.root).toEqual(expect.any(String));
+});
diff --git a/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/rspack.config.js b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/rspack.config.js
new file mode 100644
index 0000000000..934bb89c23
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/cases/lazy-bundle-fetcher/querycomponent/rspack.config.js
@@ -0,0 +1,57 @@
+// 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 { LynxEncodePlugin, LynxTemplatePlugin } from '../../../../lib/index.js';
+
+/** @type {import('@rspack/core').Configuration} */
+export default {
+ devtool: false,
+ mode: 'development',
+ plugins: [
+ new LynxEncodePlugin(),
+ new LynxTemplatePlugin({
+ ...LynxTemplatePlugin.defaultOptions,
+ intermediate: '.rspeedy/main',
+ // No `lazyBundleFetcher` — defaults to QueryComponent.
+ }),
+ /**
+ * @param {import('@rspack/core').Compiler} compiler
+ */
+ (compiler) => {
+ compiler.hooks.thisCompilation.tap('strip', (compilation) => {
+ const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(
+ compilation,
+ );
+ hooks.asyncChunkName.tap(
+ 'strip',
+ (chunkName) =>
+ chunkName.replace(':main-thread', '').replace(':background', ''),
+ );
+ });
+ },
+ /**
+ * @param {import('@rspack/core').Compiler} compiler
+ */
+ (compiler) => {
+ compiler.hooks.thisCompilation.tap('mark-mt', (compilation) => {
+ compilation.hooks.processAssets.tap(
+ {
+ name: 'mark-mt',
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,
+ },
+ (assets) => {
+ for (const name of Object.keys(assets)) {
+ if (!name.includes(':main-thread')) continue;
+ const asset = compilation.getAsset(name);
+ if (!asset) continue;
+ compilation.updateAsset(asset.name, asset.source, {
+ ...asset.info,
+ 'lynx:main-thread': true,
+ });
+ }
+ },
+ );
+ });
+ },
+ ],
+};
diff --git a/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/entry.js b/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/entry.js
new file mode 100644
index 0000000000..d0fade2b33
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/entry.js
@@ -0,0 +1,5 @@
+// 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(/* webpackChunkName: 'foo:main-thread' */ './foo.mts.js');
+import(/* webpackChunkName: 'foo:background' */ './foo.bts.js');
diff --git a/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/foo.bts.js b/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/foo.bts.js
new file mode 100644
index 0000000000..5f076d2fec
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/foo.bts.js
@@ -0,0 +1,4 @@
+// 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.
+export const layer = 'background';
diff --git a/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/foo.mts.js b/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/foo.mts.js
new file mode 100644
index 0000000000..b340bd7651
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/fixtures/lazy-bundle-fetcher/foo.mts.js
@@ -0,0 +1,4 @@
+// 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.
+export const layer = 'main-thread';
diff --git a/packages/webpack/template-webpack-plugin/test/lazy-bundle-fetcher.test.ts b/packages/webpack/template-webpack-plugin/test/lazy-bundle-fetcher.test.ts
new file mode 100644
index 0000000000..99cf6138dc
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/lazy-bundle-fetcher.test.ts
@@ -0,0 +1,157 @@
+// 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.
+
+// Output-shape tests live in `test/cases/lazy-bundle-fetcher/{fetchbundle,
+// querycomponent}/`. This file is a separate runner because the bytecode
+// gating is driven by env vars (`process.env.DEBUG`) that need to be
+// flipped per test — `cases.test.ts` runs all cases in the same process,
+// so env mutations would leak.
+
+import { mkdtempSync } from 'node:fs';
+import { tmpdir } from 'node:os';
+import { dirname, join } from 'node:path';
+
+import { afterEach, beforeEach, describe, expect, test } from '@rstest/core';
+import webpack from 'webpack';
+
+import { LynxEncodePlugin, LynxTemplatePlugin } from '../src/index.js';
+
+const FIXTURE_ENTRY = './fixtures/lazy-bundle-fetcher/entry.js';
+const CONTEXT = dirname(new URL(import.meta.url).pathname);
+
+interface CapturedEncode {
+ outputName: string;
+ customSections: Record;
+}
+
+function captureBeforeEmit() {
+ const captured: CapturedEncode[] = [];
+ const plugin = (compiler: webpack.Compiler) => {
+ compiler.hooks.thisCompilation.tap('cap', (compilation) => {
+ const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation);
+ hooks.beforeEmit.tapPromise('cap', (args) => {
+ captured.push({
+ outputName: args.outputName,
+ customSections: args.finalEncodeOptions.customSections as Record<
+ string,
+ { content: unknown; encoding?: string }
+ >,
+ });
+ return Promise.resolve(args);
+ });
+ });
+ };
+ return { captured, plugin };
+}
+
+function buildConfig(
+ capturePlugin: (compiler: webpack.Compiler) => void,
+ mode: 'development' | 'production',
+): webpack.Configuration {
+ // Each build gets its own temp output dir so parallel/serial test runs
+ // don't clobber each other (or the package's `dist/`).
+ const dist = mkdtempSync(join(tmpdir(), 'tmpl-fetchbundle-'));
+ return {
+ context: CONTEXT,
+ mode,
+ devtool: false,
+ entry: FIXTURE_ENTRY,
+ output: { iife: false, path: dist },
+ plugins: [
+ capturePlugin,
+ new LynxTemplatePlugin({
+ ...LynxTemplatePlugin.defaultOptions,
+ lazyBundleFetcher: 'FetchBundle',
+ intermediate: '.rspeedy/main',
+ }),
+ new LynxEncodePlugin(),
+ (compiler) => {
+ compiler.hooks.thisCompilation.tap('strip', (compilation) => {
+ const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(
+ compilation,
+ );
+ hooks.asyncChunkName.tap(
+ 'strip',
+ (chunkName) =>
+ chunkName.replace(':main-thread', '').replace(':background', ''),
+ );
+ });
+ },
+ (compiler) => {
+ compiler.hooks.thisCompilation.tap('mark-mt', (compilation) => {
+ compilation.hooks.processAssets.tap(
+ {
+ name: 'mark-mt',
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,
+ },
+ (assets) => {
+ for (const name of Object.keys(assets)) {
+ if (!name.includes(':main-thread')) continue;
+ const asset = compilation.getAsset(name);
+ if (!asset) continue;
+ compilation.updateAsset(asset.name, asset.source, {
+ ...asset.info,
+ 'lynx:main-thread': true,
+ });
+ }
+ },
+ );
+ });
+ },
+ ],
+ };
+}
+
+function runWebpack(config: webpack.Configuration): Promise {
+ const compiler = webpack(config);
+ return new Promise((resolve, reject) => {
+ compiler.run((err, stats) => {
+ if (err) return reject(err);
+ if (!stats) return reject(new Error('webpack returned empty stats'));
+ resolve(stats);
+ compiler.close(() => void 0);
+ });
+ });
+}
+
+async function runAndGetMtEncoding(
+ mode: 'development' | 'production',
+): Promise {
+ const { captured, plugin } = captureBeforeEmit();
+ await runWebpack(buildConfig(plugin, mode));
+ const lazy = captured.find((c) => c.outputName.startsWith('async/'));
+ return lazy?.customSections['main-thread']?.encoding;
+}
+
+describe('LynxTemplatePlugin: FetchBundle main-thread bytecode encoding', () => {
+ const originalDebug = process.env['DEBUG'];
+ void beforeEach(() => {
+ // The vitest/rstest harness sets DEBUG=rspeedy by default, which
+ // would force bytecode off in every test below. Clear it so each
+ // test opts into a specific value.
+ delete process.env['DEBUG'];
+ });
+ void afterEach(() => {
+ if (originalDebug === undefined) delete process.env['DEBUG'];
+ else process.env['DEBUG'] = originalDebug;
+ });
+
+ test('production → encoding: JsBytecode', async () => {
+ expect(await runAndGetMtEncoding('production')).toBe('JsBytecode');
+ });
+
+ test('development → no JsBytecode encoding', async () => {
+ expect(await runAndGetMtEncoding('development')).toBeUndefined();
+ });
+
+ test('DEBUG=rspeedy → no JsBytecode encoding even in production', async () => {
+ process.env['DEBUG'] = 'rspeedy';
+ expect(await runAndGetMtEncoding('production')).toBeUndefined();
+ });
+
+ test('DEBUG=other → JsBytecode encoding still on', async () => {
+ process.env['DEBUG'] = 'unrelated';
+ expect(await runAndGetMtEncoding('production')).toBe('JsBytecode');
+ });
+});
diff --git a/packages/webpack/template-webpack-plugin/test/web-encode-plugin-fetchbundle.test.ts b/packages/webpack/template-webpack-plugin/test/web-encode-plugin-fetchbundle.test.ts
new file mode 100644
index 0000000000..d16dab9e3d
--- /dev/null
+++ b/packages/webpack/template-webpack-plugin/test/web-encode-plugin-fetchbundle.test.ts
@@ -0,0 +1,121 @@
+// 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 { SyncHook } from '@rspack/lite-tapable';
+import { afterEach, beforeEach, describe, expect, test } from '@rstest/core';
+import webpack from 'webpack';
+
+import { LynxTemplatePlugin, WebEncodePlugin } from '../src/index.js';
+
+function makeFakeCompiler(): webpack.Compiler {
+ return {
+ options: { mode: 'production' },
+ hooks: {
+ thisCompilation: new SyncHook(['compilation']),
+ },
+ webpack,
+ } as unknown as webpack.Compiler;
+}
+
+function makeFakeCompilation(): webpack.Compilation {
+ return {
+ warnings: [],
+ errors: [],
+ chunks: [],
+ outputOptions: {},
+ hooks: {
+ processAssets: { tap: () => void 0 },
+ },
+ deleteAsset: () => void 0,
+ } as unknown as webpack.Compilation;
+}
+
+describe('WebEncodePlugin: lepusCode-undefined safety (FetchBundle)', () => {
+ // Force the JSON template path; the binary encoder requires more
+ // structure than these synthetic encodeOptions provide.
+ const originalBinary = process.env['EXPERIMENTAL_USE_WEB_BINARY_TEMPLATE'];
+ void beforeEach(() => {
+ process.env['EXPERIMENTAL_USE_WEB_BINARY_TEMPLATE'] = 'false';
+ });
+ void afterEach(() => {
+ if (originalBinary === undefined) {
+ delete process.env['EXPERIMENTAL_USE_WEB_BINARY_TEMPLATE'];
+ } else {
+ process.env['EXPERIMENTAL_USE_WEB_BINARY_TEMPLATE'] = originalBinary;
+ }
+ });
+
+ test('encode hook handles encodeOptions.lepusCode === undefined without crashing', async () => {
+ const compiler = makeFakeCompiler();
+ const compilation = makeFakeCompilation();
+ new WebEncodePlugin().apply(compiler);
+ compiler.hooks.thisCompilation.call(compilation, {} as never);
+
+ const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation);
+
+ // Mirror the FetchBundle shape: lepusCode is moved into customSections,
+ // so the EncodeOptions.lepusCode slot is undefined.
+ const result = await hooks.encode.promise({
+ encodeOptions: {
+ manifest: { '/main.js': 'console.log(1)' },
+ compilerOptions: {},
+ lepusCode: undefined,
+ customSections: {
+ 'main-thread': { content: '/* mts */' },
+ 'background': { content: '/* bts */' },
+ 'CSS': { encoding: 'CSS', content: { ruleList: [] } },
+ },
+ css: { cssMap: {} },
+ cardType: 'react',
+ appType: 'app',
+ pageConfig: {},
+ } as never,
+ });
+
+ expect(result).toBeDefined();
+ expect(Buffer.isBuffer(result?.buffer)).toBe(true);
+ // The emitted JSON should at minimum carry an empty lepusCode object,
+ // not crash on the undefined access we used to do.
+ const json = JSON.parse(result.buffer.toString()) as Record<
+ string,
+ unknown
+ >;
+ expect(json['lepusCode']).toEqual({});
+ expect(json['customSections']).toBeDefined();
+ });
+
+ test('legacy lepusCode-set path keeps the flattened shape', async () => {
+ const compiler = makeFakeCompiler();
+ const compilation = makeFakeCompilation();
+ new WebEncodePlugin().apply(compiler);
+ compiler.hooks.thisCompilation.call(compilation, {} as never);
+
+ const hooks = LynxTemplatePlugin.getLynxTemplatePluginHooks(compilation);
+
+ const result = await hooks.encode.promise({
+ encodeOptions: {
+ manifest: { '/main.js': 'console.log(1)' },
+ compilerOptions: {},
+ lepusCode: {
+ root: 'main lepus source',
+ lepusChunk: { worklet: 'worklet src' },
+ },
+ customSections: {},
+ css: { cssMap: {} },
+ cardType: 'react',
+ appType: 'app',
+ pageConfig: {},
+ } as never,
+ });
+
+ const json = JSON.parse(result.buffer.toString()) as Record<
+ string,
+ unknown
+ >;
+ expect(json['lepusCode']).toEqual({
+ worklet: 'worklet src',
+ root: 'main lepus source',
+ });
+ });
+});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index dbdca21b4a..2f319fc88d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -209,8 +209,8 @@ importers:
specifier: 0.0.3
version: 0.0.3
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -237,8 +237,8 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -265,8 +265,8 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -290,8 +290,8 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -315,8 +315,8 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@rsbuild/plugin-babel':
specifier: 1.1.0
version: 1.1.0(@rsbuild/core@1.7.5)
@@ -346,8 +346,8 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -371,8 +371,8 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -402,8 +402,8 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -430,11 +430,14 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
+ cross-env:
+ specifier: ^7.0.3
+ version: 7.0.3
examples/react-lazy-bundle-standalone:
dependencies:
@@ -455,11 +458,14 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
+ cross-env:
+ specifier: ^7.0.3
+ version: 7.0.3
examples/react-main-thread-function:
dependencies:
@@ -480,8 +486,8 @@ importers:
specifier: workspace:*
version: link:../../packages/rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -508,8 +514,8 @@ importers:
specifier: workspace:*
version: link:../../packages/webpack/template-webpack-plugin
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -542,8 +548,8 @@ importers:
specifier: workspace:*
version: link:../../packages/tailwind-preset
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -591,13 +597,13 @@ importers:
version: link:../a2ui-catalog-extractor
'@lynx-js/lynx-ui':
specifier: ^3.130.0
- version: 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ version: 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react':
specifier: workspace:*
version: link:../../react
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@rstest/core':
specifier: catalog:rstest
version: 0.8.1(jsdom@27.4.0)
@@ -664,8 +670,8 @@ importers:
specifier: workspace:*
version: link:../../rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@rsbuild/core':
specifier: catalog:rsbuild
version: 1.7.5
@@ -698,8 +704,8 @@ importers:
specifier: workspace:*
version: link:../../react
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -719,8 +725,8 @@ importers:
specifier: workspace:*
version: link:../../webpack/template-webpack-plugin
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
i18next:
specifier: 26.0.6
version: 26.0.6(typescript@5.9.3)
@@ -743,8 +749,8 @@ importers:
specifier: workspace:*
version: link:../../react
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@testing-library/jest-dom':
specifier: ^6.9.1
version: 6.9.1
@@ -863,8 +869,8 @@ importers:
specifier: workspace:*
version: link:../react
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
rsbuild-plugin-publint:
specifier: 0.3.4
version: 0.3.4(@rsbuild/core@2.0.0-beta.3(@module-federation/runtime-tools@0.22.0)(core-js@3.48.0))
@@ -876,8 +882,8 @@ importers:
version: '@lynx-js/internal-preact@10.29.1-20260424024911-12b794f'
devDependencies:
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@microsoft/api-extractor':
specifier: 'catalog:'
version: 7.58.2(@types/node@24.10.13)
@@ -924,8 +930,8 @@ importers:
specifier: workspace:*
version: link:../transform
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -1028,8 +1034,8 @@ importers:
specifier: 0.0.3
version: 0.0.3
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@lynx-js/web-core':
specifier: workspace:*
version: link:../web-platform/web-core
@@ -1430,8 +1436,8 @@ importers:
specifier: workspace:*
version: link:../core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@lynx-js/web-core':
specifier: workspace:*
version: link:../../web-platform/web-core
@@ -1548,8 +1554,8 @@ importers:
specifier: workspace:*
version: link:../../../rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@rsbuild/plugin-babel':
specifier: 1.1.0
version: 1.1.0(@rsbuild/core@1.7.5)
@@ -1591,8 +1597,8 @@ importers:
specifier: workspace:*
version: link:../../rspeedy/core
'@lynx-js/types':
- specifier: 3.7.0
- version: 3.7.0
+ specifier: 3.10.2-alpha.0
+ version: 3.10.2-alpha.0
'@types/react':
specifier: ^18.3.28
version: 18.3.28
@@ -3905,8 +3911,8 @@ packages:
'@lynx-js/type-element-api@0.0.3':
resolution: {integrity: sha512-e8+V1aU9VD6fmVJhS1VoE5ZxUD2udhEtTTsGBj2jlxYNscND2V6fyYFGF/dUPN+s9EwRiB80vUY/Im8tYSsMWA==}
- '@lynx-js/types@3.7.0':
- resolution: {integrity: sha512-VEcz5HBJ8m938In1VJj2phR06cWyT0Tx+HnwBJrPINiuWPjN9YfrPl1lX87XWr3eMDhKZY+6F+5eFpJ7JFgjXw==}
+ '@lynx-js/types@3.10.2-alpha.0':
+ resolution: {integrity: sha512-qJDMAw+tN4pTGFBPrVLeSndqa37v8A5f7QL/kbkGKLnO7CN270dUQjUBRxnCioLwFUkOJ6zAd+kBpcUdGb/C7A==}
'@manypkg/find-root@1.1.0':
resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==}
@@ -12809,201 +12815,201 @@ snapshots:
dependencies:
'@lezer/common': 1.5.2
- '@lynx-js/gesture-runtime@2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)':
+ '@lynx-js/gesture-runtime@2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)':
dependencies:
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@lynx-js/internal-preact@10.29.1-20260424024911-12b794f': {}
'@lynx-js/lynx-core@0.1.3': {}
- '@lynx-js/lynx-ui-button@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-button@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
clsx: 2.1.1
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-checkbox@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-checkbox@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
clsx: 2.1.1
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-common@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-common@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)
+ '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)
'@lynx-js/react': link:packages/react
'@lynx-js/react-use': 0.0.7(@lynx-js/react@packages+react)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-dialog@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-dialog@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-overlay': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-presence': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-overlay': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-presence': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
clsx: 2.1.1
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-draggable@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-draggable@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
'@lynx-js/react-use': 0.0.7(@lynx-js/react@packages+react)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-feed-list@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-feed-list@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-list': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-list': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-form@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-form@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-checkbox': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-input': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-radio-group': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-switch': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)
+ '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-checkbox': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-input': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-radio-group': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-switch': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-input@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-input@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-scroll-view': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-scroll-view': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-lazy-component@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-lazy-component@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-list@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-list@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-overlay@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-overlay@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-popover@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-popover@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-overlay': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-presence': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-overlay': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-presence': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
clsx: 2.1.1
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-presence@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-presence@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
clsx: 2.1.1
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-radio-group@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-radio-group@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
clsx: 2.1.1
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-scroll-view@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-scroll-view@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-lazy-component': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-lazy-component': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-sheet@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-sheet@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-dialog': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-overlay': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-presence': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/motion': 0.0.2(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-dialog': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-overlay': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-presence': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/motion': 0.0.2(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
clsx: 2.1.1
transitivePeerDependencies:
@@ -13011,83 +13017,83 @@ snapshots:
- react
- react-dom
- '@lynx-js/lynx-ui-sortable@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-sortable@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-draggable': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-draggable': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-swipe-action@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-swipe-action@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/gesture-runtime': 2.1.1(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-swiper@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/lynx-ui-swiper@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
'@lynx-js/react': link:packages/react
'@lynx-js/react-use': 0.0.7(@lynx-js/react@packages+react)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- react
- react-dom
- '@lynx-js/lynx-ui-switch@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)':
+ '@lynx-js/lynx-ui-switch@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)':
dependencies:
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
clsx: 2.1.1
- '@lynx-js/lynx-ui@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
- dependencies:
- '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-checkbox': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-dialog': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-draggable': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-feed-list': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-form': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-input': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-lazy-component': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-list': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-popover': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-presence': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-radio-group': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-scroll-view': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-sheet': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-sortable': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-swipe-action': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-swiper': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
- '@lynx-js/lynx-ui-switch': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(@types/react@18.3.28)
+ '@lynx-js/lynx-ui@3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ dependencies:
+ '@lynx-js/lynx-ui-button': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-checkbox': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-common': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-dialog': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-draggable': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-feed-list': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-form': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-input': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-lazy-component': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-list': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-popover': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-presence': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-radio-group': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-scroll-view': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-sheet': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-sortable': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-swipe-action': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-swiper': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
+ '@lynx-js/lynx-ui-switch': 3.130.0(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(@types/react@18.3.28)
'@lynx-js/react': link:packages/react
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
'@types/react': 18.3.28
transitivePeerDependencies:
- '@emotion/is-prop-valid'
- react
- react-dom
- '@lynx-js/motion@0.0.2(@lynx-js/react@packages+react)(@lynx-js/types@3.7.0)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
+ '@lynx-js/motion@0.0.2(@lynx-js/react@packages+react)(@lynx-js/types@3.10.2-alpha.0)(react-dom@19.2.4(react@19.2.5))(react@19.2.5)':
dependencies:
'@lynx-js/react': link:packages/react
framer-motion: 12.23.12(react-dom@19.2.4(react@19.2.5))(react@19.2.5)
motion-dom: 12.23.12
motion-utils: 12.23.6
optionalDependencies:
- '@lynx-js/types': 3.7.0
+ '@lynx-js/types': 3.10.2-alpha.0
transitivePeerDependencies:
- '@emotion/is-prop-valid'
- react
@@ -13116,7 +13122,7 @@ snapshots:
'@lynx-js/type-element-api@0.0.3': {}
- '@lynx-js/types@3.7.0':
+ '@lynx-js/types@3.10.2-alpha.0':
dependencies:
csstype: 3.1.3