Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

waitUntilExit() not resolved with syntax errors #542

Open
elmpp opened this issue Dec 14, 2022 · 3 comments · May be fixed by #563
Open

waitUntilExit() not resolved with syntax errors #542

elmpp opened this issue Dec 14, 2022 · 3 comments · May be fixed by #563
Labels

Comments

@elmpp
Copy link

elmpp commented Dec 14, 2022

  • Node 16.6.1
  • Jest 29.3.1
  • OSX Ventura 13.0.1 (22A400)

The following hangs:

import { render, Text, useApp } from 'ink'
import React, { ErrorInfo, useCallback } from 'react'

jest.setTimeout(2000)


class ErrorBoundary extends React.Component<{cb: (err: Error | null, errorInfo: ErrorInfo) => void}> {

  static getDerivedStateFromError(error: Error) {
    return null // https://tinyurl.com/y4k99pwr
  }
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    this.props.cb(error, errorInfo)
    this.setState(null) // prevents - https://github.com/reactjs/reactjs.org/issues/3028
  }
  render() {
    return this.props.children
  }
}

const MalformedComponent = () => {

  const renderSomething = () => <Text>Broken</Text>
  return (<React.Fragment key="meh">
    {renderSomething()} */} // intentional syntax error
  </React.Fragment>)
}
MalformedComponent.displayName = 'MalformedComponent'


const RenderAndExit: React.FC<{}> = () => {
  const {exit} = useApp()
  const errorBoundaryCb = useCallback(
    (err, errInfo) => {
      console.log(`errorBoundaryCb called :>> `, err, errInfo)
      exit(err)
      console.log(`errorBoundaryCb called 2 :>> `, err, errInfo)
    }
    ,
    [],
  )

  return (
    <ErrorBoundary cb={errorBoundaryCb}>
      <React.Fragment>
        <MalformedComponent />
      </React.Fragment>
    </ErrorBoundary>
  )
}

const renderWithInk = async () => {

  try {
    const {waitUntilExit} = render(<RenderAndExit />, {})
    await waitUntilExit() // hangs here (despite exit() being called). App unmount waitUntilExit is neither resolved nor rejected as expected
    console.log(`:>> not reached`);
  }
  catch (err) {
    console.log(`err :>> `, err) // not called as expected!!
  }
}

describe('render-component', () => {
  it(`app not unmounting with malformed component despite exit() being called with error`, async () => {
    await renderWithInk()
  })
})

It's strange because waitUntilExit rejects as expected if the rendered component simply throws an error like:

const MalformedComponent = () => {
  useEffect(() => {
    setTimeout(() => {
      cb(new Error('cb supplied error'))
    }, 1000)
  }, [])

  return <Text>Rendered</Text>
}
MalformedComponent.displayName = 'MalformedComponent'
@vadimdemedes
Copy link
Owner

Does error boundary catch the syntax error?

@fantua
Copy link

fantua commented Mar 9, 2023

I've encountered same issue. Can be reproduced this way:

import React from 'react';
import { render } from 'ink';


const App = () => {
  throw new Error('');

  return null;
}

(async () => {
  try {
    const { waitUntilExit } = render(<App />);
	
    await waitUntilExit()
    console.log('done');  // not reached	
  } catch (e) {
    console.log('catch');  // not reached		
  }
})();

latest ink v4.0.0

@fantua
Copy link

fantua commented Mar 11, 2023

It happens because at the moment when error is thrown waitUntilExit wasn't called. Accordingly, exitPromise wan't created and rejectExitPromise defaults to dummy function 😞

Maybe it makes sense to always init exitPromise in constructor?

@fantua fantua linked a pull request Mar 18, 2023 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants