Skip to content

Commit

Permalink
Merge pull request #152 from near/feat/widget-component
Browse files Browse the repository at this point in the history
feat: allow use of <Component />
  • Loading branch information
andy-haynes committed Dec 11, 2023
2 parents 43ce792 + c32f9da commit 99a0b6c
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 22 deletions.
4 changes: 2 additions & 2 deletions packages/compiler/src/parser.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
function parseWidgetRenders(transpiledComponent: string) {
const functionOffset = 'createElement'.length;
const componentRegex =
/createElement\(Widget,\s*\{(?:[\w\W]*?)(?:\s*src:\s*["|'](?<src>((([a-z\d]+[\-_])*[a-z\d]+\.)*([a-z\d]+[\-_])*[a-z\d]+)\/[\w.-]+))["|']/gi;
/createElement\((?:Widget|Component),\s*\{(?:[\w\W]*?)(?:\s*src:\s*["|'](?<src>((([a-z\d]+[\-_])*[a-z\d]+\.)*([a-z\d]+[\-_])*[a-z\d]+)\/[\w.-]+))["|']/gi;
return [...transpiledComponent.matchAll(componentRegex)].map((match) => {
const openParenIndex = match.index! + functionOffset;
let parenCount = 1;
Expand Down Expand Up @@ -53,7 +53,7 @@ export function parseChildComponents(
const signaturePrefix = `${componentName},{__bweMeta:{parentMeta:props.__bweMeta},`;
return componentSource.replaceAll(
expression,
expression.replace(/Widget,\s*\{/, signaturePrefix)
expression.replace(/(Widget|Component),\s*\{/, signaturePrefix)
);
},
};
Expand Down
10 changes: 7 additions & 3 deletions packages/container/src/container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export function initContainer({
parentContainerId,
trust,
updateContainerProps,
Widget, // TODO remove when <Widget /> no longer supported
},
}: InitContainerParams) {
const callbacks: { [key: string]: Function } = {};
Expand All @@ -35,16 +36,19 @@ export function initContainer({
composeSerializationMethods({
buildRequest,
callbacks,
isComponent: (c) => c === Component,
isWidget: (c) => c === Widget, // TODO remove when <Widget /> no longer supported
parentContainerId,
postCallbackInvocationMessage,
requests,
});

const { commit } = composeRenderMethods({
BWEComponent,
Component,
componentId,
Fragment,
isComponent: (c) => c === Component,
isFragment: (c) => c === Fragment,
isRootComponent: (c) => c === BWEComponent,
isWidget: (c) => c === Widget, // TODO remove when <Widget /> no longer supported
postComponentRenderMessage,
serializeNode,
trust,
Expand Down
21 changes: 13 additions & 8 deletions packages/container/src/render.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ComponentChildren, VNode } from 'preact';
import type { ComponentChildren, ComponentType, VNode } from 'preact';

import type {
BuildSafeProxyCallback,
Expand Down Expand Up @@ -50,10 +50,11 @@ interface RenderedVNode extends VNode<any> {
type DispatchRenderCallback = (vnode: VNode) => void;

export const composeRenderMethods: ComposeRenderMethodsCallback = ({
BWEComponent,
Component,
componentId,
Fragment,
isRootComponent,
isComponent,
isFragment,
isWidget, // TODO remove when <Widget /> no longer supported
postComponentRenderMessage,
serializeNode,
trust,
Expand Down Expand Up @@ -121,11 +122,11 @@ export const composeRenderMethods: ComposeRenderMethodsCallback = ({
return node;
}

if (node.type === Fragment) {
if (isFragment(node.type as ComponentType)) {
const fragmentChildren = renderedChildren || [];
if (
fragmentChildren.length === 1 &&
fragmentChildren[0]?.type === BWEComponent
isRootComponent(fragmentChildren[0]?.type as ComponentType)
) {
// this node is the root of a component defined in the container
return parseRenderedTree(
Expand All @@ -150,8 +151,12 @@ export const composeRenderMethods: ComposeRenderMethodsCallback = ({
)
: node.props;

if (typeof node.type === 'function' && node.type !== Component) {
if (isBWEComponent(node) && node.type !== BWEComponent) {
if (
typeof node.type === 'function' &&
!isComponent(node.type) &&
!isWidget(node.type)
) {
if (isBWEComponent(node) && !isRootComponent(node.type)) {
const componentNode = buildBWEComponentNode(
node as BWEComponentNode,
renderedChildren
Expand Down
16 changes: 12 additions & 4 deletions packages/container/src/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ export const composeSerializationMethods: ComposeSerializationMethodsCallback =
({
buildRequest,
callbacks,
isComponent,
isWidget,
parentContainerId,
postCallbackInvocationMessage,
requests,
Expand Down Expand Up @@ -201,16 +203,16 @@ export const composeSerializationMethods: ComposeSerializationMethodsCallback =
componentPath,
parentComponentId,
}: BuildComponentIdParams) {
// TODO warn on missing instanceId (<Widget>'s id prop) here?
// TODO warn on missing instanceId (<Component>'s id prop) here?
return [componentPath, instanceId?.toString(), parentComponentId].join(
'##'
);
}

/**
* Serialize a sandboxed <Widget /> component
* Serialize a sandboxed <Component /> component
* @param parentId ID of the parent Component
* @param props Props passed to the <Widget /> component
* @param props Props passed to the <Component /> component
*/
const serializeChildComponent = ({
parentId,
Expand Down Expand Up @@ -294,10 +296,16 @@ export const composeSerializationMethods: ComposeSerializationMethodsCallback =
});

if (typeof type === 'function') {
if (type.name !== 'Widget') {
if (!isWidget(type) && !isComponent(type)) {
throw new Error(`unrecognized Component function ${type.name}`);
}

if (isWidget(type)) {
console.warn(
'<Widget /> will be deprecated in upcoming versions of BOS Web Engine. Please update your code to reference <Component /> instead.'
);
}

const { child, placeholder } = serializeChildComponent({
parentId,
props,
Expand Down
10 changes: 7 additions & 3 deletions packages/container/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,10 +155,11 @@ export interface ComponentUpdate extends PostMessageParams {
}

interface ComposeRenderMethodsParams {
BWEComponent: FunctionComponent;
Component: Function;
componentId: string;
Fragment: FunctionComponent;
isComponent: (component: Function) => boolean;
isFragment: (component: Function) => boolean;
isRootComponent: (component: Function) => boolean;
isWidget: (component: Function) => boolean; // TODO remove when <Widget /> no longer supported
postComponentRenderMessage: PostMessageComponentRenderCallback;
serializeNode: SerializeNodeCallback;
trust: ComponentTrust;
Expand All @@ -173,6 +174,8 @@ export type ComposeRenderMethodsCallback = (
export interface ComposeSerializationMethodsParams {
buildRequest: BuildRequestCallback;
callbacks: CallbackMap;
isComponent: (component: Function) => boolean;
isWidget: (component: Function) => boolean; // TODO remove when <Widget /> no longer supported
parentContainerId: string | null;
postCallbackInvocationMessage: PostMessageComponentInvocationCallback;
requests: RequestMap;
Expand Down Expand Up @@ -236,6 +239,7 @@ export interface InitContainerParams {
props: any;
trust: ComponentTrust;
updateContainerProps: UpdateContainerPropsCallback;
Widget: Function; // TODO remove when <Widget /> no longer supported
};
}

Expand Down
6 changes: 4 additions & 2 deletions packages/iframe/src/SandboxedIframe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ function buildSandboxedComponent({
const initContainer = ${initContainer.toString()};
// placeholder to prevent <Widget /> references from breaking
// placeholder function to bind to Component/Widget references
function Component() {}
function Widget() {}
function useComponentCallback(cb, args) {
Expand Down Expand Up @@ -79,7 +80,7 @@ function buildSandboxedComponent({
},
context: {
BWEComponent,
Component: Widget,
Component,
componentId: '${id}',
componentPropsJson: ${componentPropsJson},
Fragment: Preact.Fragment,
Expand All @@ -93,6 +94,7 @@ function buildSandboxedComponent({
Preact.render(createElement(BWEComponent), document.body);
}
},
Widget,
},
});
Expand Down

0 comments on commit 99a0b6c

Please sign in to comment.