Comparing version 0.0.14 to 0.0.15
@@ -16,3 +16,8 @@ export declare type ForgoRef<T> = { | ||
}; | ||
export declare type ForgoErrorArgs = { | ||
element: ForgoElementArg; | ||
error: any; | ||
}; | ||
export declare type ForgoComponent<TProps extends ForgoElementProps> = { | ||
error?: (props: TProps, args: ForgoErrorArgs) => ForgoElement<ForgoComponentCtor<TProps>, TProps>; | ||
render: (props: TProps, args: ForgoRenderArgs) => ForgoElement<ForgoComponentCtor<TProps>, TProps>; | ||
@@ -46,2 +51,3 @@ unmount?: () => void; | ||
node: ChildNode; | ||
boundary?: ForgoComponent<any> | undefined; | ||
}; | ||
@@ -48,0 +54,0 @@ export declare function rerender(element: ForgoElementArg | undefined, props?: undefined, fullRerender?: boolean): void; |
@@ -30,3 +30,3 @@ "use strict"; | ||
*/ | ||
function internalRender(forgoNode, node, pendingAttachStates, fullRerender) { | ||
function internalRender(forgoNode, node, pendingAttachStates, fullRerender, boundary) { | ||
// Just a string | ||
@@ -38,3 +38,3 @@ if (!isForgoElement(forgoNode)) { | ||
else if (typeof forgoNode.type === "string") { | ||
return renderDOMElement(forgoNode, node, pendingAttachStates, fullRerender); | ||
return renderDOMElement(forgoNode, node, pendingAttachStates, fullRerender, boundary); | ||
} | ||
@@ -44,3 +44,3 @@ // Custom Component. | ||
else { | ||
return renderCustomComponent(forgoNode, node, pendingAttachStates, fullRerender); | ||
return renderCustomComponent(forgoNode, node, pendingAttachStates, fullRerender, boundary); | ||
} | ||
@@ -88,3 +88,3 @@ } | ||
*/ | ||
function renderDOMElement(forgoElement, node, pendingAttachStates, fullRerender) { | ||
function renderDOMElement(forgoElement, node, pendingAttachStates, fullRerender, boundary) { | ||
var _a; | ||
@@ -111,3 +111,3 @@ if (node) { | ||
attachProps(forgoElement, nodeToBindTo, pendingAttachStates); | ||
renderChildNodes(forgoElement, nodeToBindTo, fullRerender); | ||
renderChildNodes(forgoElement, nodeToBindTo, fullRerender, boundary); | ||
return { node: nodeToBindTo }; | ||
@@ -122,6 +122,21 @@ } | ||
attachProps(forgoElement, newElement, pendingAttachStates); | ||
renderChildNodes(forgoElement, newElement, fullRerender); | ||
renderChildNodes(forgoElement, newElement, fullRerender, boundary); | ||
return { node: newElement }; | ||
} | ||
} | ||
function boundaryFallback(node, props, args, statesToAttach, fullRerender, boundary, exec) { | ||
try { | ||
return exec(); | ||
} | ||
catch (error) { | ||
if (boundary && boundary.error) { | ||
const errorArgs = Object.assign(Object.assign({}, args), { error }); | ||
const newForgoElement = boundary.error(props, errorArgs); | ||
return internalRender(newForgoElement, node, statesToAttach, fullRerender, boundary); | ||
} | ||
else { | ||
throw error; | ||
} | ||
} | ||
} | ||
/* | ||
@@ -131,3 +146,3 @@ Render a Custom Component | ||
*/ | ||
function renderCustomComponent(forgoElement, node, pendingAttachStates, fullRerender) { | ||
function renderCustomComponent(forgoElement, node, pendingAttachStates, fullRerender, boundary) { | ||
if (node) { | ||
@@ -143,2 +158,3 @@ const state = getExistingForgoState(node); | ||
const component = ctor(forgoElement.props); | ||
boundary = component.error ? component : boundary; | ||
// Create new component state | ||
@@ -154,6 +170,8 @@ // ... and push it to pendingAttachStates | ||
const statesToAttach = pendingAttachStates.concat(componentState); | ||
// Create an element by rendering the component | ||
const newForgoElement = component.render(forgoElement.props, args); | ||
// Pass it on for rendering... | ||
return internalRender(newForgoElement, node, statesToAttach, fullRerender); | ||
return boundaryFallback(node, forgoElement.props, args, statesToAttach, fullRerender, boundary, () => { | ||
// Create an element by rendering the component | ||
const newForgoElement = component.render(forgoElement.props, args); | ||
// Pass it on for rendering... | ||
return internalRender(newForgoElement, node, statesToAttach, fullRerender, boundary); | ||
}); | ||
} | ||
@@ -172,7 +190,9 @@ // We have compatible state, and this is a rerender | ||
const newForgoElement = savedComponentState.component.render(forgoElement.props, args); | ||
// Pass it on for rendering... | ||
return internalRender(newForgoElement, node, statesToAttach, fullRerender); | ||
return boundaryFallback(node, forgoElement.props, args, statesToAttach, fullRerender, boundary, () => { | ||
// Pass it on for rendering... | ||
return internalRender(newForgoElement, node, statesToAttach, fullRerender, boundary); | ||
}); | ||
} | ||
else { | ||
return { node }; | ||
return { node, boundary }; | ||
} | ||
@@ -186,2 +206,3 @@ } | ||
const component = ctor(forgoElement.props); | ||
boundary = component.error ? component : boundary; | ||
// We'll have to create a new component state | ||
@@ -197,5 +218,8 @@ // ... and push it to pendingAttachStates | ||
const statesToAttach = pendingAttachStates.concat(componentState); | ||
const newForgoElement = component.render(forgoElement.props, args); | ||
// We have no node to render to yet. So pass undefined for the node. | ||
return internalRender(newForgoElement, undefined, statesToAttach, fullRerender); | ||
return boundaryFallback(undefined, forgoElement.props, args, statesToAttach, fullRerender, boundary, () => { | ||
const newForgoElement = component.render(forgoElement.props, args); | ||
// Pass it on for rendering... | ||
return internalRender(newForgoElement, undefined, statesToAttach, fullRerender, boundary); | ||
}); | ||
} | ||
@@ -216,3 +240,3 @@ } | ||
*/ | ||
function renderChildNodes(forgoElement, parentElement, fullRerender) { | ||
function renderChildNodes(forgoElement, parentElement, fullRerender, boundary) { | ||
const { children: forgoChildrenObj } = forgoElement.props; | ||
@@ -236,7 +260,7 @@ const childNodes = parentElement.childNodes; | ||
childNodes[forgoChildIndex].nodeType === TEXT_NODE_TYPE) { | ||
internalRender(stringOfPrimitiveNode(forgoChild), childNodes[forgoChildIndex], [], fullRerender); | ||
internalRender(stringOfPrimitiveNode(forgoChild), childNodes[forgoChildIndex], [], fullRerender, boundary); | ||
} | ||
// But otherwise, don't pass a replacement node. Just insert instead. | ||
else { | ||
const { node } = internalRender(stringOfPrimitiveNode(forgoChild), undefined, [], fullRerender); | ||
const { node } = internalRender(stringOfPrimitiveNode(forgoChild), undefined, [], fullRerender, boundary); | ||
parentElement.insertBefore(node, childNodes[forgoChildIndex]); | ||
@@ -243,0 +267,0 @@ } |
{ | ||
"name": "forgo", | ||
"version": "0.0.14", | ||
"version": "0.0.15", | ||
"main": "./dist", | ||
@@ -5,0 +5,0 @@ "devDependencies": { |
@@ -132,2 +132,35 @@ # forgo | ||
## Error handling | ||
By defining the error() function, Forgo lets you catch errors in child components (at any level, and not necessarily immediate children). | ||
```jsx | ||
// Here's a component which throws an error. | ||
function BadComponent() { | ||
return { | ||
render() { | ||
throw new Error("Some error occurred :("); | ||
}, | ||
}; | ||
} | ||
// Parent can catch the error by defining the error() function. | ||
function Parent( | ||
props | ||
) { | ||
return { | ||
render() { | ||
return <div><BadComponent /></div>; | ||
}, | ||
error(props, args) { | ||
return ( | ||
<p> | ||
Error in {props.name}: {args.error.message} | ||
</p> | ||
); | ||
}, | ||
}; | ||
} | ||
``` | ||
## Additional Rerender options | ||
@@ -134,0 +167,0 @@ |
144
src/index.ts
@@ -33,2 +33,7 @@ /* | ||
export type ForgoErrorArgs = { | ||
element: ForgoElementArg; | ||
error: any; | ||
}; | ||
/* | ||
@@ -40,2 +45,6 @@ ForgoComponent contains three functions. | ||
export type ForgoComponent<TProps extends ForgoElementProps> = { | ||
error?: ( | ||
props: TProps, | ||
args: ForgoErrorArgs | ||
) => ForgoElement<ForgoComponentCtor<TProps>, TProps>; | ||
render: ( | ||
@@ -148,4 +157,5 @@ props: TProps, | ||
pendingAttachStates: NodeAttachedComponentState<any>[], | ||
fullRerender: boolean | ||
): { node: ChildNode } { | ||
fullRerender: boolean, | ||
boundary?: ForgoComponent<any> | ||
): { node: ChildNode; boundary?: ForgoComponent<any> } { | ||
// Just a string | ||
@@ -166,3 +176,4 @@ if (!isForgoElement(forgoNode)) { | ||
pendingAttachStates, | ||
fullRerender | ||
fullRerender, | ||
boundary | ||
); | ||
@@ -177,3 +188,4 @@ } | ||
pendingAttachStates, | ||
fullRerender | ||
fullRerender, | ||
boundary | ||
); | ||
@@ -233,3 +245,4 @@ } | ||
pendingAttachStates: NodeAttachedComponentState<any>[], | ||
fullRerender: boolean | ||
fullRerender: boolean, | ||
boundary?: ForgoComponent<any> | ||
): { node: ChildNode } { | ||
@@ -260,3 +273,8 @@ if (node) { | ||
renderChildNodes(forgoElement, nodeToBindTo as HTMLElement, fullRerender); | ||
renderChildNodes( | ||
forgoElement, | ||
nodeToBindTo as HTMLElement, | ||
fullRerender, | ||
boundary | ||
); | ||
return { node: nodeToBindTo }; | ||
@@ -270,3 +288,3 @@ } else { | ||
attachProps(forgoElement, newElement, pendingAttachStates); | ||
renderChildNodes(forgoElement, newElement, fullRerender); | ||
renderChildNodes(forgoElement, newElement, fullRerender, boundary); | ||
return { node: newElement }; | ||
@@ -276,2 +294,30 @@ } | ||
function boundaryFallback<T>( | ||
node: ChildNode | undefined, | ||
props: any, | ||
args: ForgoRenderArgs, | ||
statesToAttach: NodeAttachedComponentState<any>[], | ||
fullRerender: boolean, | ||
boundary: ForgoComponent<any> | undefined, | ||
exec: () => T | ||
) { | ||
try { | ||
return exec(); | ||
} catch (error) { | ||
if (boundary && boundary.error) { | ||
const errorArgs = { ...args, error }; | ||
const newForgoElement = boundary.error(props, errorArgs); | ||
return internalRender( | ||
newForgoElement, | ||
node, | ||
statesToAttach, | ||
fullRerender, | ||
boundary | ||
); | ||
} else { | ||
throw error; | ||
} | ||
} | ||
} | ||
/* | ||
@@ -285,4 +331,5 @@ Render a Custom Component | ||
pendingAttachStates: NodeAttachedComponentState<any>[], | ||
fullRerender: boolean | ||
): { node: ChildNode } { | ||
fullRerender: boolean, | ||
boundary?: ForgoComponent<any> | ||
): { node: ChildNode; boundary?: ForgoComponent<any> } { | ||
if (node) { | ||
@@ -301,2 +348,3 @@ const state = getExistingForgoState(node); | ||
const component = ctor(forgoElement.props); | ||
boundary = component.error ? component : boundary; | ||
@@ -314,11 +362,22 @@ // Create new component state | ||
// Create an element by rendering the component | ||
const newForgoElement = component.render(forgoElement.props, args); | ||
// Pass it on for rendering... | ||
return internalRender( | ||
newForgoElement, | ||
return boundaryFallback( | ||
node, | ||
forgoElement.props, | ||
args, | ||
statesToAttach, | ||
fullRerender | ||
fullRerender, | ||
boundary, | ||
() => { | ||
// Create an element by rendering the component | ||
const newForgoElement = component.render(forgoElement.props, args); | ||
// Pass it on for rendering... | ||
return internalRender( | ||
newForgoElement, | ||
node, | ||
statesToAttach, | ||
fullRerender, | ||
boundary | ||
); | ||
} | ||
); | ||
@@ -332,3 +391,3 @@ } | ||
) { | ||
const args = { | ||
const args: ForgoRenderArgs = { | ||
element: { componentIndex: pendingAttachStates.length }, | ||
@@ -350,11 +409,22 @@ }; | ||
// Pass it on for rendering... | ||
return internalRender( | ||
newForgoElement, | ||
return boundaryFallback( | ||
node, | ||
forgoElement.props, | ||
args, | ||
statesToAttach, | ||
fullRerender | ||
fullRerender, | ||
boundary, | ||
() => { | ||
// Pass it on for rendering... | ||
return internalRender( | ||
newForgoElement, | ||
node, | ||
statesToAttach, | ||
fullRerender, | ||
boundary | ||
); | ||
} | ||
); | ||
} else { | ||
return { node }; | ||
return { node, boundary }; | ||
} | ||
@@ -368,2 +438,3 @@ } | ||
const component = ctor(forgoElement.props); | ||
boundary = component.error ? component : boundary; | ||
@@ -381,10 +452,22 @@ // We'll have to create a new component state | ||
const statesToAttach = pendingAttachStates.concat(componentState); | ||
const newForgoElement = component.render(forgoElement.props, args); | ||
// We have no node to render to yet. So pass undefined for the node. | ||
return internalRender( | ||
newForgoElement, | ||
return boundaryFallback( | ||
undefined, | ||
forgoElement.props, | ||
args, | ||
statesToAttach, | ||
fullRerender | ||
fullRerender, | ||
boundary, | ||
() => { | ||
const newForgoElement = component.render(forgoElement.props, args); | ||
// Pass it on for rendering... | ||
return internalRender( | ||
newForgoElement, | ||
undefined, | ||
statesToAttach, | ||
fullRerender, | ||
boundary | ||
); | ||
} | ||
); | ||
@@ -410,3 +493,4 @@ } | ||
parentElement: HTMLElement, | ||
fullRerender: boolean | ||
fullRerender: boolean, | ||
boundary?: ForgoComponent<any> | ||
) { | ||
@@ -445,3 +529,4 @@ const { children: forgoChildrenObj } = forgoElement.props; | ||
[], | ||
fullRerender | ||
fullRerender, | ||
boundary | ||
); | ||
@@ -455,3 +540,4 @@ } | ||
[], | ||
fullRerender | ||
fullRerender, | ||
boundary | ||
); | ||
@@ -458,0 +544,0 @@ parentElement.insertBefore(node, childNodes[forgoChildIndex]); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
77378
1333
356