From b5d8fff8bb9dac48573abb4f73d8984a82219956 Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Sun, 20 Feb 2022 14:35:08 +0100 Subject: [PATCH 1/4] Support preact v11 (#212) * support preact 11 * stub internal and move defaultProps to compat * remove cross env * export reusable function for tests * update tests * add changeset --- .changeset/brown-fishes-exercise.md | 5 +++ package-lock.json | 22 ++++++++----- package.json | 4 +-- src/index.js | 11 ++++--- src/util.js | 9 +++++ test/compat.test.js | 49 ++++++++++++++++++++++++++- test/render.test.js | 51 ++--------------------------- 7 files changed, 86 insertions(+), 65 deletions(-) create mode 100644 .changeset/brown-fishes-exercise.md diff --git a/.changeset/brown-fishes-exercise.md b/.changeset/brown-fishes-exercise.md new file mode 100644 index 00000000..7215082b --- /dev/null +++ b/.changeset/brown-fishes-exercise.md @@ -0,0 +1,5 @@ +--- +'preact-render-to-string': major +--- + +Support Preact v11, this breaks compat with Preact 10 diff --git a/package-lock.json b/package-lock.json index 96391807..5f99f9a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,14 +26,14 @@ "lint-staged": "^10.5.3", "microbundle": "^0.13.0", "mocha": "^8.2.1", - "preact": "^10.5.7", + "preact": "^11.0.0-experimental.0", "prettier": "^2.2.1", "sinon": "^9.2.2", "sinon-chai": "^3.5.0", "typescript": "^4.1.3" }, "peerDependencies": { - "preact": ">=10" + "preact": ">=11" } }, "node_modules/@babel/code-frame": { @@ -10664,10 +10664,14 @@ } }, "node_modules/preact": { - "version": "10.5.7", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.7.tgz", - "integrity": "sha512-4oEpz75t/0UNcwmcsjk+BIcDdk68oao+7kxcpc1hQPNs2Oo3ZL9xFz8UBf350mxk/VEdD41L5b4l2dE3Ug3RYg==", - "dev": true + "version": "11.0.0-experimental.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-11.0.0-experimental.0.tgz", + "integrity": "sha512-TXVw49O11z34ouJOe9+wzN9/ReoXoNyO1Jth48SiJDuqLKrdAiuXkBlmtTWeDgiaJr1SSPDMBdLufMDjDkb5bg==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } }, "node_modules/preferred-pm": { "version": "3.0.3", @@ -22209,9 +22213,9 @@ "dev": true }, "preact": { - "version": "10.5.7", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.7.tgz", - "integrity": "sha512-4oEpz75t/0UNcwmcsjk+BIcDdk68oao+7kxcpc1hQPNs2Oo3ZL9xFz8UBf350mxk/VEdD41L5b4l2dE3Ug3RYg==", + "version": "11.0.0-experimental.0", + "resolved": "https://registry.npmjs.org/preact/-/preact-11.0.0-experimental.0.tgz", + "integrity": "sha512-TXVw49O11z34ouJOe9+wzN9/ReoXoNyO1Jth48SiJDuqLKrdAiuXkBlmtTWeDgiaJr1SSPDMBdLufMDjDkb5bg==", "dev": true }, "preferred-pm": { diff --git a/package.json b/package.json index 9b1df539..bbcfe7e2 100644 --- a/package.json +++ b/package.json @@ -97,7 +97,7 @@ "bugs": "https://github.com/developit/preact-render-to-string/issues", "homepage": "https://github.com/developit/preact-render-to-string", "peerDependencies": { - "preact": ">=10" + "preact": ">=11" }, "devDependencies": { "@babel/plugin-transform-react-jsx": "^7.12.12", @@ -114,7 +114,7 @@ "lint-staged": "^10.5.3", "microbundle": "^0.13.0", "mocha": "^8.2.1", - "preact": "^10.5.7", + "preact": "^11.0.0-experimental.0", "prettier": "^2.2.1", "sinon": "^9.2.2", "sinon-chai": "^3.5.0", diff --git a/src/index.js b/src/index.js index 7714f36f..65f94e6d 100644 --- a/src/index.js +++ b/src/index.js @@ -4,7 +4,8 @@ import { isLargeString, styleObjToCss, assign, - getChildren + getChildren, + createInternalFromVnode } from './util'; import { options, Fragment } from 'preact'; @@ -15,8 +16,7 @@ const SHALLOW = { shallow: true }; // components without names, kept as a hash for later comparison to return consistent UnnamedComponentXX names. const UNNAMED = []; -const VOID_ELEMENTS = - /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/; +const VOID_ELEMENTS = /^(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/; const UNSAFE_NAME = /[\s\n\\/='"\0<>]/; @@ -128,14 +128,15 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) { setState: noop, forceUpdate: noop, // hooks - __h: [] + data: {} }); // options._diff if (options.__b) options.__b(vnode); + const internal = createInternalFromVnode(vnode, context); // options._render - if (options.__r) options.__r(vnode); + if (options.__r) options.__r(internal); if ( !nodeName.prototype || diff --git a/src/util.js b/src/util.js index 5ade429e..48840878 100644 --- a/src/util.js +++ b/src/util.js @@ -76,3 +76,12 @@ export function getChildren(accumulator, children) { } return accumulator; } + +export function createInternalFromVnode(vnode, context) { + return { + type: vnode.type, + props: vnode.props, + data: {}, + c: context + }; +} diff --git a/test/compat.test.js b/test/compat.test.js index e6fc6b96..e51fa096 100644 --- a/test/compat.test.js +++ b/test/compat.test.js @@ -1,5 +1,6 @@ import { render } from '../src'; -import { createElement } from 'preact/compat'; +import { h } from 'preact'; +import { createElement, Component } from 'preact/compat'; import { expect } from 'chai'; describe('compat', () => { @@ -9,4 +10,50 @@ describe('compat', () => { expect(rendered).to.equal(expected); }); + + it('should apply defaultProps (func)', () => { + const Test = (props) =>
; + Test.defaultProps = { + foo: 'default foo', + bar: 'default bar' + }; + + expect(render(), 'defaults').to.equal( + '
' + ); + expect(render(), 'partial').to.equal( + '
' + ); + expect(render(), 'overridden').to.equal( + '
' + ); + expect(render(), 'overridden').to.equal( + '
' + ); + }); + + it('should apply defaultProps (class)', () => { + class Test extends Component { + render(props) { + return
; + } + } + Test.defaultProps = { + foo: 'default foo', + bar: 'default bar' + }; + + expect(render(), 'defaults').to.equal( + '
' + ); + expect(render(), 'partial').to.equal( + '
' + ); + expect(render(), 'overridden').to.equal( + '
' + ); + expect(render(), 'overridden').to.equal( + '
' + ); + }); }); diff --git a/test/render.test.js b/test/render.test.js index 27a08f62..4bcac9f1 100644 --- a/test/render.test.js +++ b/test/render.test.js @@ -3,6 +3,7 @@ import { h, Component, createContext, Fragment, options } from 'preact'; import { useState, useContext, useEffect, useLayoutEffect } from 'preact/hooks'; import { expect } from 'chai'; import { spy, stub, match } from 'sinon'; +import { createInternalFromVnode } from '../src/util'; describe('render', () => { describe('Basic JSX', () => { @@ -322,27 +323,6 @@ describe('render', () => { match({}) ); }); - - it('should apply defaultProps', () => { - const Test = (props) =>
; - Test.defaultProps = { - foo: 'default foo', - bar: 'default bar' - }; - - expect(render(), 'defaults').to.equal( - '
' - ); - expect(render(), 'partial').to.equal( - '
' - ); - expect(render(), 'overridden').to.equal( - '
' - ); - expect(render(), 'overridden').to.equal( - '
' - ); - }); }); describe('Classical Components', () => { @@ -416,31 +396,6 @@ describe('render', () => { ); }); - it('should apply defaultProps', () => { - class Test extends Component { - render(props) { - return
; - } - } - Test.defaultProps = { - foo: 'default foo', - bar: 'default bar' - }; - - expect(render(), 'defaults').to.equal( - '
' - ); - expect(render(), 'partial').to.equal( - '
' - ); - expect(render(), 'overridden').to.equal( - '
' - ); - expect(render(), 'overridden').to.equal( - '
' - ); - }); - it('should initialize state as an empty object', () => { const fn = spy(); class Test extends Component { @@ -1113,10 +1068,10 @@ describe('render', () => { expect(calls).to.deep.equal([ ['_diff', [vnode1]], - ['_render', [vnode1]], + ['_render', [createInternalFromVnode(vnode1, {})]], ['diffed', [vnode1]], ['_diff', [vnode2]], - ['_render', [vnode2]], + ['_render', [createInternalFromVnode(vnode2, {})]], ['diffed', [vnode2]], ['_commit', [vnode1, []]] ]); From df8a69858347d54bc90d879d6ca39e699d5bee08 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sun, 9 Jul 2023 14:16:47 +0200 Subject: [PATCH 2/4] Add support for componentDidCatch --- src/index.js | 38 ++++++++++ test/render.test.js | 170 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) diff --git a/src/index.js b/src/index.js index 65f94e6d..c27f367e 100644 --- a/src/index.js +++ b/src/index.js @@ -32,6 +32,7 @@ const noop = () => {}; * @param {Boolean} [options.xml=false] If `true`, uses self-closing tags for elements without children. * @param {Boolean} [options.pretty=false] If `true`, adds whitespace for readability * @param {RegExp|undefined} [options.voidElements] RegeEx that matches elements that are considered void (self-closing) + * @param {boolean|undefined} [options.errorBoundaries=false] Enables support for error boundaries */ renderToString.render = renderToString; @@ -205,6 +206,43 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) { } if (options.diffed) options.diffed(vnode); + + if (opts.errorBoundaries && c.componentDidCatch) { + try { + return _renderToString( + rendered, + context, + opts, + opts.shallowHighOrder !== false, + isSvgMode, + selectValue + ); + } catch (err) { + c.componentDidCatch(err, {}); + + let nextState = + c._nextState !== c.state + ? c._nextState + : c.__s !== c.state + ? c.__s + : c.state; + + // Check if there is a potential state update. Note that + // we'll ignore any state updates inside cWU for now + // because that's an infinite loop anyway in the browser + if (c.state !== nextState && c.componentWillUpdate) { + c.componentWillUpdate(); + } + + // Flush potential state updates + c.state = nextState; + + rendered = c.render(c.props, c.state, c.context); + + // console.log('FAILING', c); + } + } + return _renderToString( rendered, context, diff --git a/test/render.test.js b/test/render.test.js index 4bcac9f1..5fd6f6fb 100644 --- a/test/render.test.js +++ b/test/render.test.js @@ -1137,4 +1137,174 @@ describe('render', () => { '' ); }); + + describe('Error Handling', () => { + function Thrower() { + throw new Error('fail'); + } + + function renderWithError(vnode) { + return render(vnode, {}, { errorBoundaries: true }); + } + + it('should disable error boundaries by default', () => { + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentDidCatch(error) { + this.setState({ error }); + } + + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } + } + + expect(() => render()).to.throw('fail'); + }); + + it('should invoke componentDidCatch', () => { + let args = null; + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentDidCatch(error, info) { + args = { error: error.message, info }; + this.setState({ error }); + } + + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } + } + + let res = renderWithError(); + expect(res).to.equal('

fail

'); + expect(args).to.deep.equal({ error: 'fail', info: {} }); + }); + + it("should not invoke parent's componentDidCatch", () => { + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentDidCatch(error) { + this.setState({ error }); + } + + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } + } + + let called = false; + class App extends Component { + componentDidCatch() { + called = true; + } + render() { + return ; + } + } + + renderWithError(); + expect(called).to.equal(false, "Parent's componentDidCatch was called"); + }); + + it('should invoke componentDidCatch if child throws in gDSFP', () => { + let throwerCatchCalled = false; + + class Thrower extends Component { + static getDerivedStateFromProps(props) { + throw new Error('fail'); + } + + componentDidCatch() { + throwerCatchCalled = true; + } + + render() { + return

it doesn't work

; + } + } + + let args = null; + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentDidCatch(error, info) { + args = { error: error.message, info }; + this.setState({ error }); + } + + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } + } + + let res = renderWithError(); + expect(res).to.equal('

fail

'); + expect(args).to.deep.equal({ error: 'fail', info: {} }); + + expect(throwerCatchCalled).to.equal( + false, + "Thrower's componentDidCatch should not be called" + ); + }); + + it('should invoke componentWillUpdate on state render', () => { + let calledWillUpdate = false; + + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentWillUpdate() { + calledWillUpdate = true; + } + componentDidCatch(error, info) { + this.setState({ error }); + } + + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } + } + + let res = renderWithError(); + expect(res).to.equal('

fail

'); + expect(calledWillUpdate).to.equal( + true, + 'Did not call componentWillUpdate' + ); + }); + }); }); From b7043653d7934a72c6f0e22834091123da8b73d4 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sun, 9 Jul 2023 15:29:38 +0200 Subject: [PATCH 3/4] Add support for getDerivedStateFromError --- src/index.js | 18 ++- test/render.test.js | 328 +++++++++++++++++++++++++++----------------- 2 files changed, 215 insertions(+), 131 deletions(-) diff --git a/src/index.js b/src/index.js index c27f367e..8fa99374 100644 --- a/src/index.js +++ b/src/index.js @@ -207,7 +207,10 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) { if (options.diffed) options.diffed(vnode); - if (opts.errorBoundaries && c.componentDidCatch) { + if ( + opts.errorBoundaries && + (c.componentDidCatch || nodeName.getDerivedStateFromError) + ) { try { return _renderToString( rendered, @@ -218,7 +221,16 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) { selectValue ); } catch (err) { - c.componentDidCatch(err, {}); + if (nodeName.getDerivedStateFromError) { + let nextState = nodeName.getDerivedStateFromError(err); + c.state = c._nextState = c.__s = Object.assign( + {}, + c.state, + nextState + ); + } + + if (c.componentDidCatch) c.componentDidCatch(err, {}); let nextState = c._nextState !== c.state @@ -238,8 +250,6 @@ function _renderToString(vnode, context, opts, inner, isSvgMode, selectValue) { c.state = nextState; rendered = c.render(c.props, c.state, c.context); - - // console.log('FAILING', c); } } diff --git a/test/render.test.js b/test/render.test.js index 5fd6f6fb..9654e046 100644 --- a/test/render.test.js +++ b/test/render.test.js @@ -1147,164 +1147,238 @@ describe('render', () => { return render(vnode, {}, { errorBoundaries: true }); } - it('should disable error boundaries by default', () => { - class ErrorBoundary extends Component { - constructor(props) { - super(props); - this.state = { error: null }; - } - componentDidCatch(error) { - this.setState({ error }); - } + describe('componentDidCatch', () => { + it('should disable componentDidCatch by default', () => { + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentDidCatch(error) { + this.setState({ error }); + } - render() { - return this.state.error ? ( -

{this.state.error.message}

- ) : ( - - ); + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } } - } - expect(() => render()).to.throw('fail'); - }); + expect(() => render()).to.throw('fail'); + }); - it('should invoke componentDidCatch', () => { - let args = null; - class ErrorBoundary extends Component { - constructor(props) { - super(props); - this.state = { error: null }; - } - componentDidCatch(error, info) { - args = { error: error.message, info }; - this.setState({ error }); - } + it('should invoke componentDidCatch', () => { + let args = null; + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentDidCatch(error, info) { + args = { error: error.message, info }; + this.setState({ error }); + } - render() { - return this.state.error ? ( -

{this.state.error.message}

- ) : ( - - ); + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } } - } - let res = renderWithError(); - expect(res).to.equal('

fail

'); - expect(args).to.deep.equal({ error: 'fail', info: {} }); - }); + let res = renderWithError(); + expect(res).to.equal('

fail

'); + expect(args).to.deep.equal({ error: 'fail', info: {} }); + }); - it("should not invoke parent's componentDidCatch", () => { - class ErrorBoundary extends Component { - constructor(props) { - super(props); - this.state = { error: null }; - } - componentDidCatch(error) { - this.setState({ error }); - } + it("should not invoke parent's componentDidCatch", () => { + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentDidCatch(error) { + this.setState({ error }); + } - render() { - return this.state.error ? ( -

{this.state.error.message}

- ) : ( - - ); + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } } - } - let called = false; - class App extends Component { - componentDidCatch() { - called = true; - } - render() { - return ; + let called = false; + class App extends Component { + componentDidCatch() { + called = true; + } + render() { + return ; + } } - } - renderWithError(); - expect(called).to.equal(false, "Parent's componentDidCatch was called"); - }); + renderWithError(); + expect(called).to.equal(false, "Parent's componentDidCatch was called"); + }); - it('should invoke componentDidCatch if child throws in gDSFP', () => { - let throwerCatchCalled = false; + it('should invoke componentDidCatch if child throws in gDSFP', () => { + let throwerCatchCalled = false; - class Thrower extends Component { - static getDerivedStateFromProps(props) { - throw new Error('fail'); - } + class Thrower extends Component { + static getDerivedStateFromProps(props) { + throw new Error('fail'); + } - componentDidCatch() { - throwerCatchCalled = true; - } + componentDidCatch() { + throwerCatchCalled = true; + } - render() { - return

it doesn't work

; + render() { + return

it doesn't work

; + } } - } - let args = null; - class ErrorBoundary extends Component { - constructor(props) { - super(props); - this.state = { error: null }; - } - componentDidCatch(error, info) { - args = { error: error.message, info }; - this.setState({ error }); - } + let args = null; + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentDidCatch(error, info) { + args = { error: error.message, info }; + this.setState({ error }); + } - render() { - return this.state.error ? ( -

{this.state.error.message}

- ) : ( - - ); + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } } - } - let res = renderWithError(); - expect(res).to.equal('

fail

'); - expect(args).to.deep.equal({ error: 'fail', info: {} }); + let res = renderWithError(); + expect(res).to.equal('

fail

'); + expect(args).to.deep.equal({ error: 'fail', info: {} }); - expect(throwerCatchCalled).to.equal( - false, - "Thrower's componentDidCatch should not be called" - ); - }); + expect(throwerCatchCalled).to.equal( + false, + "Thrower's componentDidCatch should not be called" + ); + }); - it('should invoke componentWillUpdate on state render', () => { - let calledWillUpdate = false; + it('should invoke componentWillUpdate on state render', () => { + let calledWillUpdate = false; - class ErrorBoundary extends Component { - constructor(props) { - super(props); - this.state = { error: null }; + class ErrorBoundary extends Component { + constructor(props) { + super(props); + this.state = { error: null }; + } + componentWillUpdate() { + calledWillUpdate = true; + } + componentDidCatch(error, info) { + this.setState({ error }); + } + + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } } - componentWillUpdate() { - calledWillUpdate = true; + + let res = renderWithError(); + expect(res).to.equal('

fail

'); + expect(calledWillUpdate).to.equal( + true, + 'Did not call componentWillUpdate' + ); + }); + }); + + describe('getDerivedStateFromError', () => { + it('should disable gDSFE by default', () => { + class ErrorBoundary extends Component { + static getDerivedStateFromError(error) { + return { error }; + } + + render() { + return this.state.error ? ( +

{this.state.error.message}

+ ) : ( + + ); + } } - componentDidCatch(error, info) { - this.setState({ error }); + + expect(() => render()).to.throw('fail'); + }); + + it('should be invoked', () => { + let calls = []; + let cDCState = null; + class ErrorBoundary extends Component { + static getDerivedStateFromError(error) { + calls.push(['gDSFE', error.message]); + return { foo: 1 }; + } + + constructor(props) { + super(props); + this.state = { error: null }; + } + + componentDidCatch(error, info) { + calls.push(['cDC', error.message]); + cDCState = this.state; + this.setState({ bar: 2 }); + } + + render() { + return this.state.foo ?

it works

: ; + } } - render() { - return this.state.error ? ( -

{this.state.error.message}

- ) : ( - - ); + let res = renderWithError(); + expect(res).to.equal('

it works

'); + expect(calls).to.deep.equal([ + ['gDSFE', 'fail'], + ['cDC', 'fail'] + ]); + expect(cDCState).to.deep.equal({ + foo: 1, + error: null + }); + }); + + it('should work without componentDidCatch', () => { + class ErrorBoundary extends Component { + static getDerivedStateFromError(error) { + return { error: error.message }; + } + + render() { + return this.state.error ?

{this.state.error}

: ; + } } - } - let res = renderWithError(); - expect(res).to.equal('

fail

'); - expect(calledWillUpdate).to.equal( - true, - 'Did not call componentWillUpdate' - ); + let res = renderWithError(); + expect(res).to.equal('

fail

'); + }); }); }); }); From 9acecb3a635ae1465195b11128b8d3062c0b9f35 Mon Sep 17 00:00:00 2001 From: Marvin Hagemeister Date: Sun, 9 Jul 2023 15:34:53 +0200 Subject: [PATCH 4/4] Update option types --- jsx.d.ts | 15 ++++++++------- src/index.d.ts | 2 ++ src/jsx.d.ts | 2 ++ 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/jsx.d.ts b/jsx.d.ts index ee2ba580..6645ee5a 100644 --- a/jsx.d.ts +++ b/jsx.d.ts @@ -1,13 +1,14 @@ import { VNode } from 'preact'; interface Options { - jsx?: boolean; - xml?: boolean; - functions?: boolean - functionNames?: boolean, - skipFalseAttributes?: boolean - pretty?: boolean | string; + jsx?: boolean; + xml?: boolean; + functions?: boolean; + functionNames?: boolean; + skipFalseAttributes?: boolean; + pretty?: boolean | string; + errorBoundaries?: boolean; } -export function render(vnode: VNode, context?: any, options?: Options):string; +export function render(vnode: VNode, context?: any, options?: Options): string; export default render; diff --git a/src/index.d.ts b/src/index.d.ts index 221d349a..30c63b30 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -4,6 +4,8 @@ interface Options { shallow?: boolean; xml?: boolean; pretty?: boolean | string; + /** Enable or disable error boundaries (default: false) */ + errorBoundaries?: boolean; } export function render(vnode: VNode, context?: any, options?: Options): string; diff --git a/src/jsx.d.ts b/src/jsx.d.ts index 98fad555..8dcafef3 100644 --- a/src/jsx.d.ts +++ b/src/jsx.d.ts @@ -3,6 +3,8 @@ import { VNode } from 'preact'; interface Options { jsx?: boolean; xml?: boolean; + /** Enable or disable error boundaries (default: false) */ + errorBoundaries?: boolean; functions?: boolean; functionNames?: boolean; skipFalseAttributes?: boolean; pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy