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

Refactor beta branch for a bit more consistency #528

Merged
merged 5 commits into from Jan 8, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/core/asyncUtils.ts
Expand Up @@ -95,4 +95,4 @@ function asyncUtils(act: Act, addResolver: (callback: () => void) => void): Asyn
}
}

export default asyncUtils
export { asyncUtils }
61 changes: 35 additions & 26 deletions src/core/index.ts
@@ -1,7 +1,7 @@
import { CreateRenderer, Renderer, RenderResult, RenderHook, RenderHookOptions } from '../types'
import { ResultContainer } from '../types/internal'

import asyncUtils from './asyncUtils'
import { asyncUtils } from './asyncUtils'
import { cleanup, addCleanup, removeCleanup } from './cleanup'

function resultContainer<TValue>(): ResultContainer<TValue> {
Expand Down Expand Up @@ -40,39 +40,48 @@ function resultContainer<TValue>(): ResultContainer<TValue> {
}
}

const createRenderHook = <TProps, TResult, TOptions extends {}, TRenderer extends Renderer<TProps>>(
function createRenderHook<TProps, TResult, TOptions extends {}, TRenderer extends Renderer<TProps>>(
createRenderer: CreateRenderer<TProps, TResult, TOptions, TRenderer>
) => (
callback: (props: TProps) => TResult,
options: RenderHookOptions<TProps, TOptions> = {} as RenderHookOptions<TProps, TOptions>
): RenderHook<TProps, TResult, TRenderer> => {
const { result, setValue, setError, addResolver } = resultContainer<TResult>()
const renderProps = { callback, setValue, setError }
let hookProps = options.initialProps
) {
const renderHook = (
callback: (props: TProps) => TResult,
options: RenderHookOptions<TProps, TOptions> = {} as RenderHookOptions<TProps, TOptions>
): RenderHook<TProps, TResult, TRenderer> => {
const { result, setValue, setError, addResolver } = resultContainer<TResult>()
const renderProps = { callback, setValue, setError }
let hookProps = options.initialProps

const { render, rerender, unmount, act, ...renderUtils } = createRenderer(renderProps, options)
const { render, rerender, unmount, act, ...renderUtils } = createRenderer(renderProps, options)

render(hookProps)
render(hookProps)

function rerenderHook(newProps = hookProps) {
hookProps = newProps
rerender(hookProps)
}
const rerenderHook = (newProps = hookProps) => {
hookProps = newProps
rerender(hookProps)
}

function unmountHook() {
removeCleanup(unmountHook)
unmount()
}
const unmountHook = () => {
removeCleanup(unmountHook)
unmount()
}

addCleanup(unmountHook)
addCleanup(unmountHook)

return {
result,
rerender: rerenderHook,
unmount: unmountHook,
...asyncUtils(act, addResolver),
...renderUtils
return {
result,
rerender: rerenderHook,
unmount: unmountHook,
...asyncUtils(act, addResolver),
...renderUtils
}
}

// If the function name does not get used before it is returned,
// it seems to vanish in nodejs and does not appear in stack traces.
// This dummy usage works around that.
const _name = renderHook.name // eslint-disable-line @typescript-eslint/no-unused-vars
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is gross and I have no idea why it is necessary, but in node, if I've got function that returns a function like so:

function createFunc() {
  function func() {
  }
  return func
}

then:

console.log(createFunc.name) // 'createFunc'
console.log(createFunc().name) // ''

But if I use func.name inside createFunc like so:

function createFunc() {
  function func() {
  }
  const name = func.name // not returned, just used
  return func
}

then:

console.log(createFunc.name) // 'createFunc'
console.log(createFunc().name) // 'func'

In the chrome console, both example will return func for the second log. It's very weird.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is really weird. Can't seem to find much online either how to potentially solve.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I've discovered that this isn't a node thing, but something in the compilation. I've narrowed it down to babel (not TS and not something in jest) as if I use kcd-scripts to compile a javascript only file, this

export function createFunc1() {
  function func() {
  }
  return func
}

export function createFunc2() {
  function func() {
  }
  const name = func.name // not returned, just used
  return func
}

becomes

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.createFunc1 = createFunc1;
exports.createFunc2 = createFunc2;

function createFunc1() {
  return function () {};
}

function createFunc2() {
  function func() {}

  func.name; // not returned, just used

  return func;
}

The name in createFunc1 has been scrubbed!

Now I just have to work out which plugin is causing this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @jpeyper. I've raised an issue in kcd-scripts about this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll keep an eye on the other issue, in the meantime, we have this workaround 🤷

mpeyper marked this conversation as resolved.
Show resolved Hide resolved

return renderHook
}

export { createRenderHook, cleanup, addCleanup, removeCleanup }
6 changes: 3 additions & 3 deletions src/dom/pure.ts
Expand Up @@ -13,18 +13,18 @@ function createDomRenderer<TProps, TResult>(
) {
const container = document.createElement('div')

const testHook = createTestHarness(rendererProps, wrapper)
const testHarness = createTestHarness(rendererProps, wrapper)

return {
render(props?: TProps) {
document.body.appendChild(container)
act(() => {
ReactDOM.render(testHook(props), container)
ReactDOM.render(testHarness(props), container)
})
},
rerender(props?: TProps) {
act(() => {
ReactDOM.render(testHook(props), container)
ReactDOM.render(testHarness(props), container)
})
},
unmount() {
Expand Down
15 changes: 12 additions & 3 deletions src/helpers/createTestHarness.tsx
Expand Up @@ -24,12 +24,12 @@ function TestComponent<TProps, TResult>({
return null
}

export const createTestHarness = <TProps, TResult>(
function createTestHarness<TProps, TResult>(
rendererProps: RendererProps<TProps, TResult>,
Wrapper?: WrapperComponent<TProps>,
suspense: boolean = true
) => {
return (props?: TProps) => {
) {
const testHarness = (props?: TProps) => {
let component = <TestComponent hookProps={props} {...rendererProps} />
if (Wrapper) {
component = <Wrapper {...(props as TProps)}>{component}</Wrapper>
Expand All @@ -39,4 +39,13 @@ export const createTestHarness = <TProps, TResult>(
}
return component
}

// If the function name does not get used before it is returned,
// it seems to vanish in nodejs and does not appear in stack traces.
// This dummy usage works around that.
const _name = testHarness.name // eslint-disable-line @typescript-eslint/no-unused-vars
mpeyper marked this conversation as resolved.
Show resolved Hide resolved
mpeyper marked this conversation as resolved.
Show resolved Hide resolved

return testHarness
}

export { createTestHarness }
10 changes: 6 additions & 4 deletions src/helpers/promises.ts
@@ -1,9 +1,11 @@
const resolveAfter = (ms: number) =>
new Promise((resolve) => {
function resolveAfter(ms: number) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}

const isPromise = <T>(value: unknown): boolean =>
typeof (value as PromiseLike<T>).then === 'function'
function isPromise<T>(value: unknown): boolean {
return typeof (value as PromiseLike<T>).then === 'function'
}

export { isPromise, resolveAfter }
8 changes: 4 additions & 4 deletions src/native/pure.ts
Expand Up @@ -7,22 +7,22 @@ import { createRenderHook, cleanup, addCleanup, removeCleanup } from '../core'
import { createTestHarness } from '../helpers/createTestHarness'

function createNativeRenderer<TProps, TResult>(
testHookProps: RendererProps<TProps, TResult>,
rendererProps: RendererProps<TProps, TResult>,
{ wrapper }: RendererOptions<TProps>
) {
let container: ReactTestRenderer

const testHook = createTestHarness(testHookProps, wrapper)
const testHarness = createTestHarness(rendererProps, wrapper)

return {
render(props?: TProps) {
act(() => {
container = create(testHook(props))
container = create(testHarness(props))
})
},
rerender(props?: TProps) {
act(() => {
container.update(testHook(props))
container.update(testHarness(props))
})
},
unmount() {
Expand Down
8 changes: 4 additions & 4 deletions src/server/pure.ts
Expand Up @@ -14,7 +14,7 @@ function createServerRenderer<TProps, TResult>(
) {
const container = document.createElement('div')

const testHook = createTestHarness(rendererProps, wrapper, false)
const testHarness = createTestHarness(rendererProps, wrapper, false)

let renderProps: TProps | undefined
let hydrated = false
Expand All @@ -23,7 +23,7 @@ function createServerRenderer<TProps, TResult>(
render(props?: TProps) {
renderProps = props
act(() => {
const serverOutput = ReactDOMServer.renderToString(testHook(props))
const serverOutput = ReactDOMServer.renderToString(testHarness(props))
container.innerHTML = serverOutput
})
},
Expand All @@ -33,7 +33,7 @@ function createServerRenderer<TProps, TResult>(
} else {
document.body.appendChild(container)
act(() => {
ReactDOM.hydrate(testHook(renderProps), container)
ReactDOM.hydrate(testHarness(renderProps), container)
})
hydrated = true
}
Expand All @@ -43,7 +43,7 @@ function createServerRenderer<TProps, TResult>(
throw new Error('You must hydrate the component before you can rerender')
}
act(() => {
ReactDOM.render(testHook(props), container)
ReactDOM.render(testHarness(props), container)
})
},
unmount() {
Expand Down