Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/blue-emus-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@lynx-js/external-bundle-rsbuild-plugin": minor
"@lynx-js/externals-loading-webpack-plugin": minor
"@lynx-js/lynx-bundle-rslib-config": minor
---

**BREAKING CHANGE**:

Simplify the API for external bundle builds by `externalsPresets` and `externalsPresetDefinitions`.
5 changes: 5 additions & 0 deletions .changeset/fix-path-posix-format.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@lynx-js/template-webpack-plugin': patch
---

use path.posix.format instead of path.format to ensure consistent path separators across platforms
5 changes: 5 additions & 0 deletions .changeset/forty-trains-carry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/devtool-connector": patch
---

fix: align GlobalKeys with Android DevToolSettings keys and filter global switch responses
5 changes: 5 additions & 0 deletions .changeset/four-forks-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lynx-js/react-umd": patch
---

Add a new `entry` export to `@lynx-js/react-umd` for reuse by wrapper libraries of `@lynx-js/react`.
5 changes: 5 additions & 0 deletions .github/externals-loading-webpack-plugin.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
applyTo: "packages/webpack/externals-loading-webpack-plugin/**"
---

Keep `ExternalsLoadingPlugin` focused on consuming finalized `externals` maps and generating runtime loading code. Do not bake project-specific preset expansion or filesystem-backed dev serving into this low-level plugin; those concerns belong in higher-level Rsbuild integrations such as `pluginExternalBundle`. It is acceptable for this low-level plugin to resolve a relative `bundlePath` against the runtime `publicPath`, because that stays within generic bundler/runtime behavior instead of Rspeedy-specific URL inference.
5 changes: 5 additions & 0 deletions .github/lynx-bundle-rslib-config.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
applyTo: "packages/rspeedy/lynx-bundle-rslib-config/**"
---

Keep `@lynx-js/lynx-bundle-rslib-config` preset resolution extensible. `output.externalsPresetDefinitions` should live alongside `output.externalsPresets`, so business configs can register new preset names such as `lynxUi` directly in their rslib config while wrappers still layer in defaults. New preset behavior should be expressed through exported preset-definition types and `extends` relationships rather than by hard-coding one-off merge logic outside the shared resolver.
9 changes: 9 additions & 0 deletions .github/plugin-external-bundle.instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
applyTo: "packages/rspeedy/plugin-external-bundle/**"
---

Keep `pluginExternalBundle` responsible for expanding built-in externals presets and for Rspeedy-specific dev-server behavior such as serving local external bundles during development. Use `externalBundleRoot` as the source directory for project-owned external bundles referenced by `bundlePath`, but treat `dist-external-bundle` as the normal default and only show `externalBundleRoot` in examples when the bundle source directory is non-default. Resolve the built-in React preset bundle from the `@lynx-js/react-umd/dev` or `@lynx-js/react-umd/prod` peer dependency instead of reaching into the monorepo with a relative filesystem path, and when the preset uses `bundlePath` rather than an explicit `url`, emit that runtime bundle into the user's build output automatically.
Document `bundlePath` as the preferred option over `url` in both public README examples and API docs. README examples for external-bundle workflows should show `externalsPresets.reactlynx` and `externalsPresetDefinitions` instead of hand-written React externals maps or custom middleware, and should prefer the shorthand `externals: { './App.js': 'comp.lynx.bundle' }` form when request keys and section names naturally align.
Treat `externalsPresetDefinitions` as the main extension point for internal wrappers or downstream variants. When a wrapper needs to add aliases like `@byted-lynx/*`, extend the OSS plugin through preset definitions instead of forking the whole plugin implementation.
Because the built-in `reactlynx` preset resolves `@lynx-js/react-umd/dev` or `@lynx-js/react-umd/prod` to generated `dist/*` artifacts, add precise package-level Turbo dependencies for the affected `test` or `build` tasks instead of widening the global workspace `test` pipeline; otherwise clean CI runs can fail even when local machines pass due to stale built files already existing.
When `@lynx-js/react-umd` builds both development and production bundles into the same `dist` directory, keep `output.cleanDistPath: false` in its rslib config so `react-dev.lynx.bundle` and `react-prod.lynx.bundle` coexist; otherwise the second build silently removes the first export target and breaks `@lynx-js/react-umd/dev`.
1 change: 1 addition & 0 deletions examples/react-externals/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist-external-bundle
6 changes: 2 additions & 4 deletions examples/react-externals/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@

In this example, we show:

- Use `@lynx-js/lynx-bundle-rslib-config` to bundle ReactLynx runtime to a separate Lynx bundle.
- Use `@lynx-js/lynx-bundle-rslib-config` to bundle a simple ReactLynx component library to a separate Lynx bundle.
- Use `@lynx-js/external-bundle-rsbuild-plugin` to load ReactLynx runtime (sync) and component bundle (async).
- Use `@lynx-js/external-bundle-rsbuild-plugin` to load the built-in ReactLynx runtime bundle (sync) and component bundle (async).

## Usage

```bash
pnpm build:reactlynx
pnpm build:comp-lib
pnpm dev
```

The dev server will automatically serve the ReactLynx runtime and the component library bundles.
The dev server will automatically serve the built-in ReactLynx runtime bundle and the component library bundle.
178 changes: 4 additions & 174 deletions examples/react-externals/lynx.config.ts
Original file line number Diff line number Diff line change
@@ -1,91 +1,12 @@
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

import { pluginExternalBundle } from '@lynx-js/external-bundle-rsbuild-plugin';
import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
import { defineConfig } from '@lynx-js/rspeedy';
import type { RsbuildPlugin } from '@lynx-js/rspeedy';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

function getIPAddress() {
const interfaces = os.networkInterfaces();
for (const devName in interfaces) {
const iface = interfaces[devName];
if (iface) {
for (const alias of iface) {
if (
alias.family === 'IPv4' && alias.address !== '127.0.0.1'
&& !alias.internal
) {
return alias.address;
}
}
}
}
return 'localhost';
}

const enableBundleAnalysis = !!process.env['RSPEEDY_BUNDLE_ANALYSIS'];
const PORT = 8291;
const EXTERNAL_BUNDLE_PREFIX = process.env['EXTERNAL_BUNDLE_PREFIX']
?? `http://${getIPAddress()}:${PORT}`;

const pluginServeExternals = (): RsbuildPlugin => ({
name: 'serve-externals',
setup(api) {
api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {
return mergeRsbuildConfig(config, {
dev: {
setupMiddlewares: [
(middlewares) => {
middlewares.unshift((
req: import('node:http').IncomingMessage,
res: import('node:http').ServerResponse,
next: () => void,
) => {
if (req.url === '/react.lynx.bundle') {
const bundlePath = path.resolve(
__dirname,
'../../packages/react-umd/dist/react-dev.lynx.bundle',
);
if (fs.existsSync(bundlePath)) {
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Access-Control-Allow-Origin', '*');
fs.createReadStream(bundlePath).pipe(res);
return;
}
}
if (req.url === '/comp-lib.lynx.bundle') {
const bundlePath = path.resolve(
__dirname,
'./dist/comp-lib.lynx.bundle',
);
if (fs.existsSync(bundlePath)) {
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Access-Control-Allow-Origin', '*');
fs.createReadStream(bundlePath).pipe(res);
return;
}
}
next();
});
return middlewares;
},
],
},
});
});
},
});

export default defineConfig({
plugins: [
pluginServeExternals(),
pluginReactLynx(),
pluginQRCode({
schema(url) {
Expand All @@ -94,98 +15,11 @@ export default defineConfig({
},
}),
pluginExternalBundle({
externalsPresets: {
reactlynx: true,
},
externals: {
'@lynx-js/react': {
libraryName: ['ReactLynx', 'React'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/internal': {
libraryName: ['ReactLynx', 'ReactInternal'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/jsx-dev-runtime': {
libraryName: ['ReactLynx', 'ReactJSXDevRuntime'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/jsx-runtime': {
libraryName: ['ReactLynx', 'ReactJSXRuntime'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/lepus/jsx-dev-runtime': {
libraryName: ['ReactLynx', 'ReactJSXLepusDevRuntime'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/lepus/jsx-runtime': {
libraryName: ['ReactLynx', 'ReactJSXLepusRuntime'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/experimental/lazy/import': {
libraryName: ['ReactLynx', 'ReactLazyImport'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/legacy-react-runtime': {
libraryName: ['ReactLynx', 'ReactLegacyRuntime'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/runtime-components': {
libraryName: ['ReactLynx', 'ReactComponents'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/worklet-runtime/bindings': {
libraryName: ['ReactLynx', 'ReactWorkletRuntime'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'@lynx-js/react/debug': {
libraryName: ['ReactLynx', 'ReactDebug'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
preact: {
libraryName: ['ReactLynx', 'Preact'],
url: `${EXTERNAL_BUNDLE_PREFIX}/react.lynx.bundle`,
background: { sectionPath: 'ReactLynx' },
mainThread: { sectionPath: 'ReactLynx__main-thread' },
async: false,
},
'./App.js': {
libraryName: 'CompLib',
url: `${EXTERNAL_BUNDLE_PREFIX}/comp-lib.lynx.bundle`,
background: { sectionPath: 'CompLib' },
mainThread: { sectionPath: 'CompLib__main-thread' },
async: true,
},
'./App.js': 'comp-lib.lynx.bundle',
},
globalObject: 'globalThis',
}),
Expand All @@ -198,11 +32,7 @@ export default defineConfig({
},
},
},
server: {
port: PORT,
},
output: {
filenameHash: 'contenthash:8',
cleanDistPath: false,
},
});
2 changes: 1 addition & 1 deletion examples/react-externals/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"type": "module",
"scripts": {
"build": "npm run build:comp-lib && npm run build:reactlynx && rspeedy build",
"build": "pnpm run build:comp-lib && pnpm run build:reactlynx && rspeedy build",
"build:comp-lib": "rslib build --config rslib-comp-lib.config.ts",
"build:comp-lib:dev": "cross-env NODE_ENV=development rslib build --config rslib-comp-lib.config.ts",
"build:reactlynx": "pnpm --filter @lynx-js/react-umd build",
Expand Down
31 changes: 3 additions & 28 deletions examples/react-externals/rslib-comp-lib.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,15 @@ export default defineExternalBundleRslibConfig({
id: 'comp-lib',
source: {
entry: {
'CompLib': './external-bundle/CompLib.tsx',
'./App.js': './external-bundle/CompLib.tsx',
},
},
plugins: [
pluginReactLynx(),
],
output: {
cleanDistPath: false,
dataUriLimit: Number.POSITIVE_INFINITY,
externals: {
'@lynx-js/react': ['ReactLynx', 'React'],
'@lynx-js/react/internal': ['ReactLynx', 'ReactInternal'],
'@lynx-js/react/jsx-dev-runtime': ['ReactLynx', 'ReactJSXDevRuntime'],
'@lynx-js/react/jsx-runtime': ['ReactLynx', 'ReactJSXRuntime'],
'@lynx-js/react/lepus/jsx-dev-runtime': [
'ReactLynx',
'ReactJSXLepusDevRuntime',
],
'@lynx-js/react/lepus/jsx-runtime': ['ReactLynx', 'ReactJSXLepusRuntime'],
'@lynx-js/react/experimental/lazy/import': [
'ReactLynx',
'ReactLazyImport',
],
'@lynx-js/react/legacy-react-runtime': [
'ReactLynx',
'ReactLegacyRuntime',
],
'@lynx-js/react/runtime-components': ['ReactLynx', 'ReactComponents'],
'@lynx-js/react/worklet-runtime/bindings': [
'ReactLynx',
'ReactWorkletRuntime',
],
'@lynx-js/react/debug': ['ReactLynx', 'ReactDebug'],
'preact': ['ReactLynx', 'Preact'],
externalsPresets: {
reactlynx: true,
},
globalObject: 'globalThis',
},
Expand Down
2 changes: 1 addition & 1 deletion examples/react-externals/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from '@lynx-js/react';
import { useCallback, useEffect, useState } from 'react';

import './App.css';
import arrow from './assets/arrow.png';
Expand Down
7 changes: 6 additions & 1 deletion examples/react-externals/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import '@lynx-js/react/debug';
import { Fragment } from 'react';

import { root } from '@lynx-js/react';

import { App } from './App.js';

import './index.css';

root.render(
<App />,
// biome-ignore lint/style/useFragmentSyntax: Just to demonstrate import react is external
<Fragment>
<App />,
</Fragment>,
);

if (import.meta.webpackHot) {
Expand Down
13 changes: 13 additions & 0 deletions examples/react-externals/turbo.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"$schema": "https://turbo.build/schema.json",
"extends": [
"//"
],
"tasks": {
"build": {
"dependsOn": [
"@lynx-js/react-umd#build"
]
}
}
}
2 changes: 1 addition & 1 deletion packages/lynx/benchx_cli/scripts/build_unix.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ if [ ! -f "package.json" ] || ! grep -q '"name": "benchx_cli"' package.json; the
exit 1
fi

LOCKED_VERSION="benchx_cli-202602132156"
LOCKED_VERSION="benchx_cli-202603021542"

# Check if binary is up to date
if [ -f "./dist/bin/benchx_cli" ] && [ -f "./dist/bin/benchx_cli.version" ]; then
Expand Down
Loading
Loading