Skip to content

Commit

Permalink
Backport all changes
Browse files Browse the repository at this point in the history
  • Loading branch information
lemonmade committed Jan 4, 2024
1 parent 1e1cb50 commit a6064f2
Show file tree
Hide file tree
Showing 34 changed files with 1,276 additions and 302 deletions.
2 changes: 1 addition & 1 deletion examples/kitchen-sink-vite/app/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ receiver.connect(uiRoot);
// provides a `render()` function that will be called in response to this
// method, with the `Endpoint` taking care of serializing arguments over
// `postMessage()` to the remote context.
await workerSandbox.render(receiver.receive, {
await workerSandbox.render(receiver.connection, {
sandbox: 'worker',
framework: 'htm',
async alert(content) {
Expand Down
4 changes: 2 additions & 2 deletions examples/kitchen-sink-vite/app/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {RemoteMutationCallback} from '@remote-dom/core';
import type {RemoteConnection} from '@remote-dom/core';

export type RenderFramework = 'vanilla' | 'htm';
export type RenderSandbox = 'iframe' | 'worker';
Expand All @@ -10,5 +10,5 @@ export interface RenderApi {
}

export interface SandboxApi {
render(callback: RemoteMutationCallback, api: RenderApi): Promise<unknown>;
render(connection: RemoteConnection, api: RenderApi): Promise<unknown>;
}
2 changes: 1 addition & 1 deletion examples/minimal-iframes/app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@

window.addEventListener('message', ({source, data}) => {
if (source !== iframe.contentWindow) return;
receiver.receive(data);
receiver.connection.mutate(data);
});
</script>
</body>
Expand Down
2 changes: 1 addition & 1 deletion examples/vanilla-dom/app/host.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@ receiver.bind(uiRoot);
// provides a `render()` function that will be called in response to this
// method, with the `Endpoint` taking care of serializing arguments over
// `postMessage()` to the remote context.
await remoteEndpoint.call.render(receiver.receive, {
await remoteEndpoint.call.render(receiver.connection, {
getMessage: () => textField.value,
});
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"@quilted/typescript": "^0.3.0",
"@quilted/vite": "^0.1.17",
"@types/node": "~20.9.0",
"jsdom": "^23.0.1",
"prettier": "^3.1.0",
"rollup": "^4.9.0",
"tsx": "^4.7.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ import {
MUTATION_TYPE_UPDATE_PROPERTY,
} from './constants.ts';
import type {
RemoteMutationCallback,
RemoteConnection,
RemoteMutationRecord,
RemoteMutationRecordInsertChild,
RemoteMutationRecordRemoveChild,
RemoteMutationRecordUpdateText,
RemoteMutationRecordUpdateProperty,
} from './types.ts';

export type {RemoteMutationCallback};
export type {RemoteConnection};

export interface RemoteMutationHandler {
export interface RemoteConnectionHandler {
call: RemoteConnection['call'];
insertChild(
id: RemoteMutationRecordInsertChild[1],
child: RemoteMutationRecordInsertChild[2],
Expand All @@ -36,22 +37,26 @@ export interface RemoteMutationHandler {
): void;
}

export function createRemoteMutationCallback({
export function createRemoteConnection({
call,
insertChild,
removeChild,
updateText,
updateProperty,
}: RemoteMutationHandler): RemoteMutationCallback {
}: RemoteConnectionHandler): RemoteConnection {
const messageMap = new Map<RemoteMutationRecord[0], (...args: any[]) => any>([
[MUTATION_TYPE_INSERT_CHILD, insertChild],
[MUTATION_TYPE_REMOVE_CHILD, removeChild],
[MUTATION_TYPE_UPDATE_TEXT, updateText],
[MUTATION_TYPE_UPDATE_PROPERTY, updateProperty],
]);

return function handler(records) {
for (const [type, ...args] of records) {
messageMap.get(type)!(...args);
}
return {
call,
mutate(records) {
for (const [type, ...args] of records) {
messageMap.get(type)!(...args);
}
},
};
}
2 changes: 1 addition & 1 deletion packages/core/source/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ export const MUTATION_TYPE_UPDATE_TEXT = 2;
export const MUTATION_TYPE_UPDATE_PROPERTY = 3;

export const REMOTE_ID = Symbol.for('remote.id');
export const REMOTE_CALLBACK = Symbol.for('remote.callback');
export const REMOTE_CONNECTION = Symbol.for('remote.connection');
export const REMOTE_PROPERTIES = Symbol.for('remote.properties');
export const ROOT_ID = '_root';
3 changes: 2 additions & 1 deletion packages/core/source/elements.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export {
disconnectRemoteNode,
serializeRemoteNode,
updateRemoteElementProperty,
callRemoteElementMethod,
} from './elements/internals.ts';

export {remoteSlots} from './elements/decorators/remote-slots.ts';
Expand All @@ -32,4 +33,4 @@ export {customElement} from './elements/decorators/custom-element.ts';

export {BooleanOrString} from './elements/property-types/BooleanOrString.ts';

export type {RemoteMutationCallback} from './callback.ts';
export type {RemoteConnection} from './types.ts';
87 changes: 65 additions & 22 deletions packages/core/source/elements/RemoteElement.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import {REMOTE_PROPERTIES} from '../constants.ts';
import {RemoteEvent} from './RemoteEvent.ts';
import {updateRemoteElementProperty} from './internals.ts';
import {
updateRemoteElementProperty,
callRemoteElementMethod,
} from './internals.ts';

export interface RemoteElementPropertyType<Value = unknown> {
parse?(value: string | unknown): Value;
Expand All @@ -24,7 +27,7 @@ export interface RemoteElementPropertyDefinition<Value = unknown> {
default?: Value;
}

interface NormalizedRemoteElementPropertyDefinition<Value = unknown> {
interface RemoteElementPropertyNormalizedDefinition<Value = unknown> {
name: string;
type: RemoteElementPropertyTypeOrBuiltIn<Value>;
alias?: string[];
Expand All @@ -43,7 +46,7 @@ export type RemoteElementPropertiesDefinition<

export interface RemoteElementSlotDefinition {}

interface NormalizedRemoteElementSlotDefinition {}
interface RemoteElementSlotNormalizedDefinition {}

export type RemoteElementSlotsDefinition<
Slots extends Record<string, any> = {},
Expand All @@ -52,35 +55,42 @@ export type RemoteElementSlotsDefinition<
};

export type RemotePropertiesFromElementConstructor<T> = T extends {
new (): RemoteElement<infer Properties, any>;
new (): RemoteElement<infer Properties, any, any>;
}
? Properties
: never;

export type RemoteSlotsFromElementConstructor<T> = T extends {
new (): RemoteElement<any, infer Slots>;
new (): RemoteElement<any, infer Slots, any>;
}
? Slots
: never;

export type RemoteMethodsFromElementConstructor<T> = T extends {
new (): RemoteElement<any, any, infer Methods>;
}
? Methods
: never;

export type RemoteElementConstructor<
Properties extends Record<string, any> = {},
Slots extends Record<string, any> = {},
Methods extends Record<string, (...args: any[]) => any> = {},
> = {
new (): RemoteElement<Properties, Slots> & Properties;
new (): RemoteElement<Properties, Slots, Methods> & Properties & Methods;
readonly remoteSlots?:
| RemoteElementSlotsDefinition<Slots>
| readonly (keyof Slots)[];
readonly remoteSlotDefinitions: Map<
string,
NormalizedRemoteElementSlotDefinition
RemoteElementSlotNormalizedDefinition
>;
readonly remoteProperties?:
| RemoteElementPropertiesDefinition<Properties>
| readonly (keyof Properties)[];
readonly remotePropertyDefinitions: Map<
string,
NormalizedRemoteElementPropertyDefinition
RemoteElementPropertyNormalizedDefinition
>;
createProperty<Value = unknown>(
name: string,
Expand All @@ -91,25 +101,51 @@ export type RemoteElementConstructor<
export interface RemoteElementCreatorOptions<
Properties extends Record<string, any> = {},
Slots extends Record<string, any> = {},
Methods extends Record<string, (...args: any[]) => any> = {},
> {
slots?: RemoteElementConstructor<Properties, Slots>['remoteSlots'];
properties?: RemoteElementConstructor<Properties, Slots>['remoteProperties'];
slots?: RemoteElementConstructor<Properties, Slots, Methods>['remoteSlots'];
properties?: RemoteElementConstructor<
Properties,
Slots,
Methods
>['remoteProperties'];
methods?: Methods | keyof Methods[];
}

export function createRemoteElement<
Properties extends Record<string, any> = {},
Slots extends Record<string, any> = {},
Methods extends Record<string, (...args: any[]) => any> = {},
>({
slots,
properties,
methods,
}: RemoteElementCreatorOptions<
Properties,
Slots
> = {}): RemoteElementConstructor<Properties, Slots> {
return class extends RemoteElement<Properties, Slots> {
Slots,
Methods
> = {}): RemoteElementConstructor<Properties, Slots, Methods> {
const RemoteElementConstructor = class extends RemoteElement<
Properties,
Slots
> {
static readonly remoteSlots = slots;
static readonly remoteProperties = properties;
} as any;

if (methods != null) {
if (Array.isArray(methods)) {
for (const method of methods) {
RemoteElementConstructor.prototype[method] = function (...args: any[]) {
return callRemoteElementMethod(this, method, ...args);
};
}
} else {
Object.assign(RemoteElementConstructor.prototype, methods);
}
}

return RemoteElementConstructor;
}

const SLOT_PROPERTY = 'slot';
Expand All @@ -131,26 +167,28 @@ type RemoteEventListenerRecord = [
export abstract class RemoteElement<
Properties extends Record<string, any> = {},
Slots extends Record<string, any> = {},
Methods extends Record<string, (...args: any[]) => any> = {},
> extends HTMLElement {
static readonly slottable = true;

static readonly remoteSlots?: any;
static readonly remoteProperties?: any;
static readonly remoteMethods?: any;

static get observedAttributes() {
return this.finalize().__observedAttributes;
}

static get remotePropertyDefinitions(): Map<
string,
NormalizedRemoteElementPropertyDefinition
RemoteElementPropertyNormalizedDefinition
> {
return this.finalize().__remotePropertyDefinitions;
}

static get remoteSlotDefinitions(): Map<
string,
NormalizedRemoteElementSlotDefinition
RemoteElementSlotNormalizedDefinition
> {
return this.finalize().__remoteSlotDefinitions;
}
Expand All @@ -161,11 +199,11 @@ export abstract class RemoteElement<
private static readonly __eventToPropertyMap = new Map<string, string>();
private static readonly __remotePropertyDefinitions = new Map<
string,
NormalizedRemoteElementPropertyDefinition
RemoteElementPropertyNormalizedDefinition
>();
private static readonly __remoteSlotDefinitions = new Map<
string,
NormalizedRemoteElementSlotDefinition
RemoteElementSlotNormalizedDefinition
>();

static createProperty<Value = unknown>(
Expand All @@ -183,6 +221,7 @@ export abstract class RemoteElement<
}

protected static finalize(): typeof this {
// eslint-disable-next-line no-prototype-builtins
if (this.hasOwnProperty('__finalized')) {
return this;
}
Expand All @@ -200,11 +239,11 @@ export abstract class RemoteElement<
const eventToPropertyMap = new Map<string, string>();
const remoteSlotDefinitions = new Map<
string,
NormalizedRemoteElementSlotDefinition
RemoteElementSlotNormalizedDefinition
>();
const remotePropertyDefinitions = new Map<
string,
NormalizedRemoteElementPropertyDefinition
RemoteElementPropertyNormalizedDefinition
>();

if (typeof SuperConstructor.finalize === 'function') {
Expand Down Expand Up @@ -308,6 +347,9 @@ export abstract class RemoteElement<
/** @internal */
__properties?: Properties;

/** @internal */
__methods?: Methods;

private [REMOTE_PROPERTIES]!: Properties;
private [REMOTE_EVENTS]?: {
readonly events: Map<string, RemoteEventRecord>;
Expand Down Expand Up @@ -338,6 +380,7 @@ export abstract class RemoteElement<

// Don’t override actual accessors. This is handled by the
// `remoteProperty()` decorator applied to the accessor.
// eslint-disable-next-line no-prototype-builtins
if (Object.getPrototypeOf(this).hasOwnProperty(property)) {
continue;
}
Expand Down Expand Up @@ -527,7 +570,7 @@ function saveRemoteProperty<Value = unknown>(
observedAttributes: string[],
remotePropertyDefinitions: Map<
string,
NormalizedRemoteElementPropertyDefinition
RemoteElementPropertyNormalizedDefinition
>,
attributeToPropertyMap: Map<string, string>,
eventToPropertyMap: Map<string, string>,
Expand Down Expand Up @@ -592,7 +635,7 @@ function saveRemoteProperty<Value = unknown>(
eventToPropertyMap.set(eventName, name);
}

const definition: NormalizedRemoteElementPropertyDefinition = {
const definition: RemoteElementPropertyNormalizedDefinition = {
name,
type,
alias,
Expand Down Expand Up @@ -620,7 +663,7 @@ function convertAttributeValueToProperty<Value = unknown>(

switch (type) {
case Boolean:
return value !== 'false';
return value != null && value !== 'false';
case Object:
case Array:
try {
Expand Down
Loading

0 comments on commit a6064f2

Please sign in to comment.