Skip to content

Commit

Permalink
Polyfill
Browse files Browse the repository at this point in the history
  • Loading branch information
lemonmade committed Feb 6, 2024
1 parent 0461e8b commit 72ccc19
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 27 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,11 @@ First, we’ll create the remote environment’s version of `ui-button`. The rem
createThreadFromInsideIframe({
expose: {
connect(connection) {
// This `render()` method will kick off the process of synchronizing
// changes between environments. It will be called on the host with a
// `RemoteConnection` object, which you’ll generally get from one of
// Remote DOM’s `Receiver` classes.
render(connection) {
retain(connection);
const observer = new RemoteMutationObserver(connection);
observer.observe(root);
Expand Down Expand Up @@ -308,7 +312,7 @@ Finally, we need to provide a “real” implementation of our `ui-button` eleme
// Like our previous example, we need to use a library that can serialize
// function properties over `postMessage`.
const thread = createThreadFromIframe(iframe);
thread.connect(receiver.connection);
thread.render(receiver.connection);
</script>
</body>
</html>
Expand Down
9 changes: 2 additions & 7 deletions packages/core/source/polyfill.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import {
hooks,
Window,
installWindowAsGlobal,
type Hooks,
} from '@remote-dom/polyfill';
import {hooks, Window, type Hooks} from '@remote-dom/polyfill';

import {
REMOTE_CONNECTION,
Expand All @@ -23,7 +18,7 @@ import {

const window = new Window();

installWindowAsGlobal(window);
Window.setGlobal(window);

hooks.insertChild = (parent, node, index) => {
const connection = (parent as RemoteConnectedNode)[REMOTE_CONNECTION];
Expand Down
44 changes: 44 additions & 0 deletions packages/polyfill/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,45 @@
# `@remote-dom/polyfill`

A polyfill for the browser APIs used by Remote DOM. This allows you to use Remote DOM in environments that don’t have a native DOM, like [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API).

This package provides a low-level polyfill, with hooks that allow other libraries to intercept changes to the DOM. Unless you know you need this package, you probably want to import from [`@remote-dom/core/polyfill`](../core/README.md#remote-domcorepolyfill) instead, which uses the hooks provided by this library to automatically synchronize remote elements between environments.

## Installation

```sh
npm install @remote-dom/polyfill --save # npm
pnpm install @remote-dom/polyfill --save # pnpm
yarn add @remote-dom/polyfill # yarn
```

## Usage

This package provides a `Window` class, which implements a limited subset of the [`Window` browser interface](https://developer.mozilla.org/en-US/docs/Web/API/Window). You’ll create an instance of the `Window` class and install it to the global environment using the `Window.setGlobal()` method.

```ts
import {Window} from '@remote-dom/polyfill';

const window = new Window();
Window.setGlobal(window);

// Now you can use many important DOM APIs, like `document` and `Element`:
const div = document.createElement('div');
```

This process will install polyfilled versions of the following globals:

- [`window`](https://developer.mozilla.org/en-US/docs/Web/API/Window/window), [`parent`](https://developer.mozilla.org/en-US/docs/Web/API/Window/parent), [`top`](https://developer.mozilla.org/en-US/docs/Web/API/Window/top), and [`self`](https://developer.mozilla.org/en-US/docs/Web/API/Window/self) which are all references to the `Window` instance (`self` is only overwritten when it is not already defined, to avoid overwriting the Web Worker `self` binding).
- [`document`](https://developer.mozilla.org/en-US/docs/Web/API/Window/document)
- [`customElements`](https://developer.mozilla.org/en-US/docs/Web/API/Window/customElements)
- [`location`](https://developer.mozilla.org/en-US/docs/Web/API/Window/location) and [`navigator`](https://developer.mozilla.org/en-US/docs/Web/API/Window/navigator), though these are just set to `globalThis.location` and `globalThis.navigator`.
- The [`Event`](https://developer.mozilla.org/en-US/docs/Web/API/Event), [`EventTarget`](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget), [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent), [`Node`](https://developer.mozilla.org/en-US/docs/Web/API/Node), [`ParentNode`](https://developer.mozilla.org/en-US/docs/Web/API/ParentNode), [`ChildNode`](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode), [`Document`](https://developer.mozilla.org/en-US/docs/Web/API/Document), [`DocumentFragment`](https://developer.mozilla.org/en-US/docs/Web/API/DocumentFragment), [`CharacterData`](https://developer.mozilla.org/en-US/docs/Web/API/CharacterData), [`Comment`](https://developer.mozilla.org/en-US/docs/Web/API/Comment), [`Text`](https://developer.mozilla.org/en-US/docs/Web/API/Text), [`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element), [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement), [`SVGElement`](https://developer.mozilla.org/en-US/docs/Web/API/SVGElement), [`HTMLTemplateElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement), and [`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) constructors.

This polyfill lets you hook into many of the operations that happen in the DOM, like creating elements, updating attributes, and adding event listeners. You define these hooks by overwriting any of the properties on the `hooks` export of this library.

```ts
import {hooks} from '@remote-dom/polyfill';

hooks.createElement = (element) => {
console.log('Creating element:', element);
};
```
1 change: 1 addition & 0 deletions packages/polyfill/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "@remote-dom/polyfill",
"type": "module",
"license": "MIT",
"description": "A polyfill for the browser APIs used by Remote DOM",
"publishConfig": {
"access": "public",
"@remote-dom/registry": "https://registry.npmjs.org"
Expand Down
34 changes: 17 additions & 17 deletions packages/polyfill/source/Window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export class Window extends EventTarget {
location = globalThis.location;
navigator = globalThis.navigator;
Event = Event;
EventTarget = EventTarget;
CustomEvent = CustomEvent;
Node = Node;
ParentNode = ParentNode;
ChildNode = ChildNode;
EventTarget = EventTarget;
CustomEvent = CustomEvent;
DocumentFragment = DocumentFragment;
Document = Document;
CharacterData = CharacterData;
Expand All @@ -41,24 +41,24 @@ export class Window extends EventTarget {
SVGElement = SVGElement;
HTMLTemplateElement = HTMLTemplateElement;
MutationObserver = MutationObserver;
}

export function installWindowAsGlobal(window: Window) {
const properties = Object.getOwnPropertyDescriptors(window);
static setGlobal(window: Window) {
const properties = Object.getOwnPropertyDescriptors(window);

delete (properties as any).self;
delete (properties as any).self;

Object.defineProperties(globalThis, {
...properties,
window: {value: window},
});
Object.defineProperties(globalThis, {
...properties,
window: {value: window},
});

if (typeof self === 'undefined') {
Object.defineProperty(globalThis, 'self', {value: window});
} else {
// There can already be a `self`, like when polyfilling the DOM
// in a Web Worker. In those cases, just mirror all the `Window`
// properties onto `self`, rather than wholly redefining it.
Object.defineProperties(self, properties);
if (typeof self === 'undefined') {
Object.defineProperty(globalThis, 'self', {value: window});
} else {
// There can already be a `self`, like when polyfilling the DOM
// in a Web Worker. In those cases, just mirror all the `Window`
// properties onto `self`, rather than wholly redefining it.
Object.defineProperties(self, properties);
}
}
}
2 changes: 1 addition & 1 deletion packages/polyfill/source/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export {hooks, type Hooks} from './hooks.ts';
export {Window, installWindowAsGlobal} from './Window.ts';
export {Window} from './Window.ts';

0 comments on commit 72ccc19

Please sign in to comment.