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
17 changes: 17 additions & 0 deletions .changeset/rozenite-lynx-merge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@rozenite/lynx': minor
'@rozenite/lynx-dev': minor
---

Rozenite for Lynx is now one package. `@rozenite/lynx` now exports both the
device runtime (`.`, unchanged) and the rspeedy/Rsbuild dev-server plugin
(`./rspeedy`, previously `@rozenite/lynx-dev`). Install just `@rozenite/lynx`
and add `rozeniteLynxPlugin` from `@rozenite/lynx/rspeedy` to your
`lynx.config.ts` — the plugin now injects the device runtime for you, only
in development, so there is nothing left to import by hand in your app's own
source (and no way to accidentally ship it to production).

`@rozenite/lynx-dev` is deprecated and now re-exports `@rozenite/lynx/rspeedy`
for backwards compatibility; existing `import { rozeniteLynxPlugin } from
'@rozenite/lynx-dev'` code keeps working but should migrate to
`@rozenite/lynx/rspeedy`.
11 changes: 5 additions & 6 deletions apps/playground-lynx/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ Edit `src/App.tsx` to see updates — the page hot-reloads as you save.

## Rozenite integration

- [`@rozenite/lynx-dev`](../../packages/lynx-dev) is added to
`lynx.config.ts`. It discovers installed plugins and bridges Lynx's
DebugRouter to the CDP dialect `@rozenite/app` speaks.
- [`@rozenite/lynx`](../../packages/lynx) is imported once in `src/index.tsx`.
It installs the device-side dispatcher that plugins talk to, and must run
before any plugin hook does.
- [`@rozenite/lynx/rspeedy`](../../packages/lynx) is added to
`lynx.config.ts`. It discovers installed plugins, bridges Lynx's
DebugRouter to the CDP dialect `@rozenite/app` speaks, and injects the
device-side dispatcher plugins talk to — there is nothing to import in
`src/index.tsx`.

## Plugins

Expand Down
2 changes: 1 addition & 1 deletion apps/playground-lynx/lynx.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { defineConfig } from '@lynx-js/rspeedy';
import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin';
import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin';
import { pluginTypeCheck } from '@rsbuild/plugin-type-check';
import { rozeniteLynxPlugin } from '@rozenite/lynx-dev';
import { rozeniteLynxPlugin } from '@rozenite/lynx/rspeedy';

export default defineConfig({
plugins: [
Expand Down
3 changes: 1 addition & 2 deletions apps/playground-lynx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"@lynx-js/react": "^0.125.0",
"@rozenite/controls-plugin": "workspace:*",
"@rozenite/feature-flags-plugin": "workspace:*",
"@rozenite/lynx": "workspace:*",
"@rozenite/rhf-plugin": "workspace:*",
"@rozenite/tanstack-query-plugin": "workspace:*",
"@tanstack/react-query": "^5.81.5",
Expand All @@ -26,7 +25,7 @@
"@lynx-js/react-rsbuild-plugin": "^0.19.1",
"@lynx-js/rspeedy": "^0.16.5",
"@lynx-js/types": "4.1.0",
"@rozenite/lynx-dev": "workspace:*",
"@rozenite/lynx": "workspace:*",
"@rsbuild/plugin-type-check": "1.6.0",
"@types/react": "^19.2.18",
"eslint": "^9.25.0",
Expand Down
4 changes: 0 additions & 4 deletions apps/playground-lynx/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import '@lynx-js/preact-devtools';
import '@lynx-js/react/debug';
// Installs the device-side dispatcher that Rozenite plugins talk to. Must run
// before any plugin's `useRozeniteDevToolsClient` does, so it is imported
// ahead of `App`.
import '@rozenite/lynx';
import { root } from '@lynx-js/react';

import { App } from './App.jsx';
Expand Down
136 changes: 14 additions & 122 deletions packages/lynx-dev/README.md
Original file line number Diff line number Diff line change
@@ -1,43 +1,17 @@
![rozenite-banner](https://www.rozenite.dev/rozenite-banner.jpg)

### An rspeedy/Rsbuild plugin for integrating React Native DevTools plugins into your Lynx development workflow.
### Deprecated: `@rozenite/lynx-dev` has been merged into `@rozenite/lynx`.

[![mit licence][license-badge]][license] [![npm downloads][npm-downloads-badge]][npm-downloads] [![Chat][chat-badge]][chat] [![PRs Welcome][prs-welcome-badge]][prs-welcome]

`@rozenite/lynx-dev` brings Rozenite to [Lynx](https://lynxjs.org). It runs a small dev server on top of your rspeedy/Rsbuild dev server that speaks Metro's inspector dialect — `/json/list` and `/inspector/debug` — so the same `@rozenite/app` DevTools frontend React Native uses connects to a Lynx app unmodified. Underneath, it discovers Lynx apps over [DebugRouter](https://github.com/lynx-family/lynx/tree/main/devtool) and bridges DebugRouter's wire protocol to Chrome DevTools Protocol (CDP) on the fly.

## Features

- **Automatic Plugin Discovery**: discovers installed Rozenite plugins from your project's `package.json`, exactly as `@rozenite/metro` does for React Native
- **Metro-Compatible Dev Server**: serves `/json/list` and `/inspector/debug` so `@rozenite/app` needs no Lynx-specific code
- **DebugRouter Bridge**: discovers Lynx apps over USB and translates DebugRouter frames to and from CDP
- **Reload-Safe Device Ids**: a device's identity survives an app reload, so a saved DevTools tab reconnects instead of going stale
- **Express Middleware**: provides the same scoped, composable middleware shape as `@rozenite/metro`

## Installation

Install the plugin as a development dependency:

```bash
npm install --save-dev @rozenite/lynx-dev
```

Your app also needs [`@rozenite/lynx`](https://www.npmjs.com/package/@rozenite/lynx) — the small runtime that installs the device-side dispatcher Rozenite plugins talk to. Import it once at your app's entry point:

```ts
if (__DEV__) {
require('@rozenite/lynx');
}
```

## Usage

Add the plugin to your `lynx.config.ts`:
`@rozenite/lynx-dev` and `@rozenite/lynx` are now a single package. Install
[`@rozenite/lynx`](https://www.npmjs.com/package/@rozenite/lynx) and import
the plugin from `@rozenite/lynx/rspeedy` instead:

```ts
// lynx.config.ts
import { defineConfig } from '@lynx-js/rspeedy';
import { rozeniteLynxPlugin } from '@rozenite/lynx-dev';
import { rozeniteLynxPlugin } from '@rozenite/lynx/rspeedy';

export default defineConfig({
plugins: [
Expand All @@ -47,98 +21,16 @@ export default defineConfig({
});
```

### With Custom Options

```ts
// lynx.config.ts
import { defineConfig } from '@lynx-js/rspeedy';
import { rozeniteLynxPlugin } from '@rozenite/lynx-dev';

export default defineConfig({
plugins: [
rozeniteLynxPlugin({
include: ['@my-org/my-plugin', 'another-plugin'],
exclude: ['unwanted-plugin'],
destroyOnDetachPlugins: ['@rozenite/network-activity-plugin'],
pluginDisplay: 'sidebar',
}),
],
});
```

Start your rspeedy dev server as usual, plug in a Lynx app, and Rozenite will log a DevTools URL for it in the terminal as soon as it connects.

## Configuration

### `RozeniteLynxOptions`

```typescript
type RozeniteLynxOptions = {
enabled?: boolean; // Whether to enable Rozenite. Defaults to `process.env.NODE_ENV !== 'production'`
include?: string[]; // Only load these specific plugins
exclude?: string[]; // Exclude these plugins from loading
destroyOnDetachPlugins?: string[]; // Plugins that should be destroyed when switching panels
pluginDisplay?: 'sidebar' | 'tabs'; // How plugins are displayed in DevTools
deviceSerial?: string; // Restrict device discovery to one physical device serial/udid
enableAndroid?: boolean; // Discover physical Android devices over adb. Default: true
enableIOS?: boolean; // Discover physical iOS devices over usbmux. Default: true
enableHarmony?: boolean; // Discover physical HarmonyOS devices. Default: false
enableDesktop?: boolean; // Discover targets on localhost, including simulators. Default: true
};
```

**Options:**

- `enabled` - Whether to enable Rozenite (optional, defaults to disabled in production builds)
- `include` - Array of package names to explicitly include (optional)
- `exclude` - Array of package names to exclude from loading (optional)
- `destroyOnDetachPlugins` - Array of package names that should be destroyed when switching panels instead of maintaining their state (optional, by default all plugins persist their state)
- `pluginDisplay` - Use `'sidebar'` (default) to show all plugin panels in one Rozenite tab, or `'tabs'` to retain a separate DevTools tab for every plugin panel
- `deviceSerial`, `enableAndroid`, `enableIOS`, `enableHarmony`, `enableDesktop` - Control which devices DebugRouter discovers

`enableAndroid` and `enableIOS` cover **physical** devices only, over adb and usbmux. Simulators and emulators are ordinary processes on your machine, so DebugRouter inside them is reachable on `127.0.0.1:8901-8919` — which is what `enableDesktop` scans. That is why it defaults to on; turning it off is how you stop seeing simulators.

## Plugin Discovery

Rozenite plugins are discovered from your project's `package.json` the exact same way they are for React Native:

1. **Scanning node_modules**: Searches all node_modules directories in the project
2. **Checking for Rozenite manifest**: Looks for the Rozenite manifest file in each package
3. **Validating plugin structure**: Ensures the plugin has the required build output
4. **Loading plugin metadata**: Extracts plugin information for integration

## Plugin Requirements

For a package to be recognized as a Rozenite plugin, it must:

1. **Have a Rozenite manifest**: `dist/rozenite.json` file
2. **Be properly built**: Plugin assets must be available in the `dist` directory
3. **Follow naming conventions**: Package name should not start with `.`
4. **Be accessible**: Package must be readable from node_modules

## Troubleshooting

### No plugins found

- Ensure plugins are properly installed in `node_modules`
- Check that plugins have been built and contain `dist/rozenite.json`
- Verify plugin package names are not excluded in configuration

### No DevTools URL is logged

- Make sure `@rozenite/lynx` is imported at your app's entry point, guarded for development builds
- Make sure the Lynx app is actually connected over USB, and DebugRouter can see it
- Check that `enabled` was not explicitly set to `false`, and that you are not running a production build (`rozeniteLynxPlugin` never runs during `rspeedy build`)

### Device reconnects but DevTools disconnects

- This is what the plugin's stable `logicalDeviceId` and reload-robustness handling exist to prevent — if it still happens, please open an issue with your Lynx SDK version and platform

## Requirements
You no longer need to import `@rozenite/lynx` (or this package) anywhere in
your app's own source — `@rozenite/lynx/rspeedy` now injects the device
runtime for you, only in development. See
[`@rozenite/lynx`'s README](https://www.npmjs.com/package/@rozenite/lynx) for
the full option list and current documentation.

- Node.js >= 20.19.0
- rspeedy / Rsbuild-based Lynx project
- Installed Rozenite plugins
This package remains available, re-exporting `@rozenite/lynx/rspeedy`
unchanged, so existing `import { rozeniteLynxPlugin } from '@rozenite/lynx-dev'`
code keeps working. It will be removed in a future major version — please
migrate when convenient.

## Made with ❤️ at Callstack

Expand Down
12 changes: 2 additions & 10 deletions packages/lynx-dev/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@rozenite/lynx-dev",
"version": "2.3.0",
"description": "Rozenite dev-server integration for Lynx: a DebugRouter to CDP bridge and an rspeedy plugin.",
"description": "Deprecated: merged into @rozenite/lynx. Install @rozenite/lynx and import from @rozenite/lynx/rspeedy instead.",
"keywords": [
"bundler",
"devtools",
Expand Down Expand Up @@ -45,17 +45,9 @@
"test": "vitest --run --passWithNoTests"
},
"dependencies": {
"@lynx-js/debug-router-connector": "^0.0.15",
"@rozenite/middleware": "workspace:*",
"@rozenite/tools": "workspace:*",
"express": "^5.1.0",
"tslib": "^2.3.0",
"ws": "^8.18.3"
"@rozenite/lynx": "workspace:*"
},
"devDependencies": {
"@rsbuild/core": "2.1.10",
"@types/express": "^5.0.3",
"@types/ws": "^8.18.1",
"vitest": "^4.0.18"
},
"engines": {
Expand Down
Loading