From 155a0d8a97c9fa51e41e0b1d2c3f23bd08046ab2 Mon Sep 17 00:00:00 2001 From: Chris Sauve Date: Tue, 6 Feb 2024 02:16:30 -0500 Subject: [PATCH] Add a `remote-ui` migration guide --- .../migrations/remote-ui-to-remote-dom.md | 297 ++++++++++++++++++ packages/react/README.md | 4 +- 2 files changed, 299 insertions(+), 2 deletions(-) create mode 100644 documentation/migrations/remote-ui-to-remote-dom.md diff --git a/documentation/migrations/remote-ui-to-remote-dom.md b/documentation/migrations/remote-ui-to-remote-dom.md new file mode 100644 index 00000000..8be1e43e --- /dev/null +++ b/documentation/migrations/remote-ui-to-remote-dom.md @@ -0,0 +1,297 @@ +# Migrating from `remote-ui` to Remote DOM + +Remote DOM started out with as a project called `remote-ui`. The [original packages](https://www.npmjs.com/package/@remote-ui/core) had a DOM-like API, but because they did not use the DOM directly, it was difficult to use them with any JavaScript library other than React. Remote DOM is a complete rewrite of `remote-ui` that uses the DOM directly, which makes for more seamless use of browser-friendly libraries, and a simpler learning code for developers who already have familiarity with the DOM. + +> **Note**: These benefits are shown in code in the [“kitchen sink” example](/examples/kitchen-sink/), where we write the same “remote” rendering code using many different techniques and JavaScript libraries. + +This guide will go over how you can migrate from `remote-ui` to Remote DOM. + +## Uninstall `@remote-ui` packages and install their `@remote-dom` equivalents + +Almost all projects will need to swap out the `@remote-ui/core` package for `@remote-dom/core`: + +```diff +{ + "dependencies": { +- "@remote-ui/core": "^2.2.0", ++ "@remote-dom/core": "^1.0.0", + } +} +``` + +If you use [`@remote-ui/react`](https://www.npmjs.com/package/@remote-ui/react), you’ll need to make some additional changes, which we’ll cover [later in this guide](#update-react-integration) + +The following `@remote-ui` packages have been removed, and have no `@remote-dom` equivalent: + +- [`@remote-ui/rpc`](https://www.npmjs.com/package/@remote-ui/rpc). This package was removed to simplify the repo, but you will likely still need a library that provides similar functionality, since Remote DOM depends on a smart RPC library for synchronizing event listeners and other function calls over `postMessage()`. You can continue using `@remote-ui/rpc` with `@remote-dom/core`, or you can switch to a different library that can communicate functions over `postMessage()`. [`@quilted/threads`](https://www.npmjs.com/package/@quilted/threads) is a more modern alternative that is actively maintained, or you can use another popular community library like [`comlink`](https://www.npmjs.com/package/comlink). +- [`@remote-ui/async-subscriptions`](https://www.npmjs.com/package/@remote-ui/async-subscriptions). Like `@remote-ui/rpc`, you can continue using this package with `@remote-dom/core`, or you can switch to a different library that provides similar functionality. The `@quilted/threads` package contains a more modern alternative, which allows you to [synchronize a Preact Signal over `postMessage()`](https://github.com/lemonmade/quilt/blob/main/packages/threads/README.md#preact-signals). +- [`@remote-ui/web-workers`](https://www.npmjs.com/package/@remote-ui/web-workers). You need to use a different library or build tool to create web worker sandboxes in which to run Remote DOM-powered code. +- [`@remote-ui/dom`](https://www.npmjs.com/package/@remote-ui/dom). The [`@remote-dom/core` package](/packages/core/) now provides all DOM-related utilities. +- [`@remote-ui/htm`](https://www.npmjs.com/package/@remote-ui/htm). The [`@remote-dom/core/html`](/packages/core/README.md#remote-domcorehtml) package exports an [`htm`](https://www.npmjs.com/package/htm) function for building trees of DOM nodes. +- [`@remote-ui/mini-react`](https://www.npmjs.com/package/@remote-ui/react). This library was an adapted version of [Preact](https://preactjs.com/), which is a popular alternative to React. The new DOM-based API works great with Preact, so you can use Preact directly instead. We’ve also introduced a [`@remote-dom/preact`](/packages/preact/) package that provides a few convenience utilities for using Preact with Remote DOM. +- [`@remote-ui/vue`](https://www.npmjs.com/package/@remote-ui/vue). This library was always poorly maintained. The new DOM-based API works well with Vite without any additional configuration, so we’ve removed this dedicated integration. +- [`@remote-ui/traversal`](https://www.npmjs.com/package/@remote-ui/traversal). The tree traversal utilities provided by this library are all supported natively by the DOM. +- [`@remote-ui/testing`](https://www.npmjs.com/package/@remote-ui/testing). There are many other great libraries for testing DOM code. + +## Import the DOM polyfill + +If you are running your Remote DOM-dependent code in a Web Worker sandbox, you will need a limited subset of the global DOM API available for Remote DOM to work. Add the following import as one of the first lines in your Web Worker code: + +```ts +import '@remote-dom/core/polyfill'; +``` + +## Define components as custom elements + +In `remote-ui`, “components” were referenced by their name, by passing a string to the `RemoteRoot.createComponent()` function. In Remote DOM, components are instead [defined as custom elements](/packages/core/README.md#remote-domcoreelements). In the remote environment, you’ll need to define a custom element with the set of properties and methods that your component accepts. + +```ts +// Define each component that your +class Button extends RemoteElement { + static get remoteProperties() { + return { + tooltip: {type: String}, + primary: {type: Boolean}, + onPress: {event: true}, + }; + } +} + +customElements.define('ui-button', Button); +``` + +## Update your host code to use the new `Receiver` classes + +`remote-ui` provided a [`createRemoteReceiver()` utility](https://github.com/Shopify/remote-ui/tree/main/packages/core#createremotereceiver) for creating the object that will receive updates from the remote environment. In Remote DOM, this utility is replaced with the [`RemoteReceiver`](/packages/core/README.md#remotereceiver), [`DOMRemoteReceiver`](/packages/core/README.md#domremotereceiver), or [`SignalRemoteReceiver`](/packages/signals/README.md#signalremotereceiver) classes. + +```ts +// Replace this: + +import {createRemoteReceiver} from '@remote-ui/core'; + +const receiver = createRemoteReceiver(); +sendToRemoteEnvironment(receiver.receive); + +// With this: + +import {RemoteReceiver} from '@remote-dom/core'; +import {retain, release} from '@quilted/threads'; + +// You now need to pass in functions to manage the memory for functions manually, +// where this was previously done automatically in `@remote-ui/rpc`. +const receiver = new RemoteReceiver({retain, release}); +sendToRemoteEnvironment(receiver.connection); +``` + +## Update your remote code to use DOM elements instead of the `RemoteRoot` object + +`remote-ui` provided a [`createRemoteRoot()` utility](https://github.com/Shopify/remote-ui/tree/main/packages/core#createremoteroot) for creating the root node for a tree of remote elements. This root node managed the top-level children of the tree, and had methods for creating components to be inserted into the tree. In Remote DOM, this API is removed in favor of native DOM APIs. + +```ts +// Replace this: + +import {createRemoteRoot} from '@remote-ui/core'; + +export function receiveChannelFromHostEnvironment(channel) { + const root = createRemoteRoot(channel); + const button = root.createComponent( + 'Button', + { + primary: true, + onPress: () => console.log('Pressed!'), + }, + ['Press me!'], + ); + root.appendChild(button); +} + +// With this: + +import {RemoteRootElement} from '@remote-dom/core/elements'; + +// Define our `Button` custom element, from earlier. +customElements.define('ui-button', Button); + +// If you’re using an `