From d068194127106d0b107cac778f5a49d486a79d94 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 6 Sep 2019 16:48:06 +0200 Subject: [PATCH 01/47] rename files to TS --- packages/react-async/src/{Async.js => Async.tsx} | 0 packages/react-async/src/{globalScope.js => globalScope.ts} | 0 packages/react-async/src/{helpers.js => helpers.tsx} | 0 packages/react-async/src/{index.js => index.ts} | 0 packages/react-async/src/{propTypes.js => propTypes.ts} | 0 packages/react-async/src/{reducer.js => reducer.ts} | 0 packages/react-async/src/{status.js => status.ts} | 0 packages/react-async/src/{useAsync.js => useAsync.tsx} | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename packages/react-async/src/{Async.js => Async.tsx} (100%) rename packages/react-async/src/{globalScope.js => globalScope.ts} (100%) rename packages/react-async/src/{helpers.js => helpers.tsx} (100%) rename packages/react-async/src/{index.js => index.ts} (100%) rename packages/react-async/src/{propTypes.js => propTypes.ts} (100%) rename packages/react-async/src/{reducer.js => reducer.ts} (100%) rename packages/react-async/src/{status.js => status.ts} (100%) rename packages/react-async/src/{useAsync.js => useAsync.tsx} (100%) diff --git a/packages/react-async/src/Async.js b/packages/react-async/src/Async.tsx similarity index 100% rename from packages/react-async/src/Async.js rename to packages/react-async/src/Async.tsx diff --git a/packages/react-async/src/globalScope.js b/packages/react-async/src/globalScope.ts similarity index 100% rename from packages/react-async/src/globalScope.js rename to packages/react-async/src/globalScope.ts diff --git a/packages/react-async/src/helpers.js b/packages/react-async/src/helpers.tsx similarity index 100% rename from packages/react-async/src/helpers.js rename to packages/react-async/src/helpers.tsx diff --git a/packages/react-async/src/index.js b/packages/react-async/src/index.ts similarity index 100% rename from packages/react-async/src/index.js rename to packages/react-async/src/index.ts diff --git a/packages/react-async/src/propTypes.js b/packages/react-async/src/propTypes.ts similarity index 100% rename from packages/react-async/src/propTypes.js rename to packages/react-async/src/propTypes.ts diff --git a/packages/react-async/src/reducer.js b/packages/react-async/src/reducer.ts similarity index 100% rename from packages/react-async/src/reducer.js rename to packages/react-async/src/reducer.ts diff --git a/packages/react-async/src/status.js b/packages/react-async/src/status.ts similarity index 100% rename from packages/react-async/src/status.js rename to packages/react-async/src/status.ts diff --git a/packages/react-async/src/useAsync.js b/packages/react-async/src/useAsync.tsx similarity index 100% rename from packages/react-async/src/useAsync.js rename to packages/react-async/src/useAsync.tsx From 5665d4a956d8986de60e1ed59294c1f5a13e86b4 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 6 Sep 2019 16:57:01 +0200 Subject: [PATCH 02/47] convert react-async to TS --- babel.config.js | 6 +- jest.config.js | 4 +- package.json | 5 +- packages/react-async/package.json | 16 +- packages/react-async/src/Async.tsx | 251 +++++++++++++++++++++--- packages/react-async/src/globalScope.ts | 16 +- packages/react-async/src/helpers.tsx | 93 +++++++-- packages/react-async/src/reducer.ts | 66 ++++--- packages/react-async/src/status.ts | 18 +- packages/react-async/src/useAsync.tsx | 167 ++++++++++++---- packages/react-async/tsconfig.json | 70 +++++++ packages/react-async/yarn.lock | 8 + yarn.lock | 17 +- 13 files changed, 600 insertions(+), 137 deletions(-) create mode 100644 packages/react-async/tsconfig.json create mode 100644 packages/react-async/yarn.lock diff --git a/babel.config.js b/babel.config.js index 49a48f94..4e9750dd 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,11 +1,11 @@ module.exports = { presets: ["@babel/preset-react"], - plugins: ["@babel/plugin-proposal-object-rest-spread"], + plugins: ["@babel/plugin-proposal-object-rest-spread", "@babel/plugin-proposal-class-properties"], env: { test: { - presets: ["@babel/preset-env", "@babel/preset-react"], - plugins: ["@babel/plugin-transform-runtime"], + presets: ["@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript"], + plugins: ["@babel/plugin-transform-runtime", "@babel/plugin-proposal-class-properties"], }, }, } diff --git a/jest.config.js b/jest.config.js index ef3484d5..6205c683 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,7 +17,9 @@ module.exports = { coverageDirectory: "/coverage", verbose: true, bail: true, - transform: { "^.+\\.js$": "babel-jest" }, + transform: { + "^.+\\.[tj]s$": "babel-jest", + }, projects: ["/packages/*"], setupFiles: ["/jest.setup.js"], testPathIgnorePatterns: ["/node_modules/", "/pkg/"], diff --git a/package.json b/package.json index edad7461..98ab0d14 100644 --- a/package.json +++ b/package.json @@ -36,15 +36,18 @@ }, "devDependencies": { "@babel/core": "7.5.5", + "@babel/plugin-proposal-class-properties": "^7.5.5", "@babel/plugin-proposal-object-rest-spread": "7.5.5", "@babel/plugin-transform-runtime": "7.5.5", "@babel/preset-env": "7.5.5", "@babel/preset-react": "7.0.0", + "@babel/preset-typescript": "^7.3.3", "@pika/pack": "0.5.0", "@pika/plugin-build-node": "0.6.0", "@pika/plugin-build-types": "0.6.0", "@pika/plugin-build-web": "0.6.0", "@pika/plugin-standard-pkg": "0.6.0", + "@pika/plugin-ts-standard-pkg": "^0.6.0", "@storybook/react": "5.2.0-rc.2", "@testing-library/jest-dom": "4.1.0", "@testing-library/react": "9.1.3", @@ -70,4 +73,4 @@ "react-dom": "16.9.0", "storybook-chromatic": "^2.2.2" } -} +} \ No newline at end of file diff --git a/packages/react-async/package.json b/packages/react-async/package.json index dc05db7d..a6824eb4 100644 --- a/packages/react-async/package.json +++ b/packages/react-async/package.json @@ -29,23 +29,17 @@ "@pika/pack": { "pipeline": [ [ - "@pika/plugin-standard-pkg", - { - "exclude": [ - "specs.js", - "*.spec.js" - ] - } + "@pika/plugin-ts-standard-pkg" ], [ "@pika/plugin-build-node" ], [ "@pika/plugin-build-web" - ], - [ - "@pika/plugin-build-types" ] ] + }, + "devDependencies": { + "typescript": "^3.6.2" } -} +} \ No newline at end of file diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index 8a5733e3..0215b2b0 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -1,19 +1,187 @@ -import React from "react" +import React, { ComponentClass } from "react" import globalScope from "./globalScope" import { IfInitial, IfPending, IfFulfilled, IfRejected, IfSettled } from "./helpers" +// @ts-ignore import propTypes from "./propTypes" import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" +export declare type AsyncChildren = ((state: AsyncState) => React.ReactNode) | React.ReactNode +export declare type InitialChildren = + | ((state: AsyncInitial) => React.ReactNode) + | React.ReactNode +export declare type PendingChildren = + | ((state: AsyncPending) => React.ReactNode) + | React.ReactNode +export declare type FulfilledChildren = + | ((data: T, state: AsyncFulfilled) => React.ReactNode) + | React.ReactNode +export declare type RejectedChildren = + | ((error: Error, state: AsyncRejected) => React.ReactNode) + | React.ReactNode +export declare type SettledChildren = + | ((state: AsyncFulfilled | AsyncRejected) => React.ReactNode) + | React.ReactNode + +export declare type PromiseFn = (props: AsyncProps, controller: AbortController) => Promise +export declare type DeferFn = ( + args: any[], + props: AsyncProps, + controller: AbortController +) => Promise + +export interface AbstractAction { + type: string + meta: { counter: number; [meta: string]: any } +} +export type Meta = AbstractAction["meta"] + +export declare type Start = AbstractAction & { type: "start"; payload: () => Promise } +export declare type Cancel = AbstractAction & { type: "cancel" } +export declare type Fulfill = AbstractAction & { type: "fulfill"; payload: T } +export declare type Reject = AbstractAction & { type: "reject"; payload: Error; error: true } +export declare type AsyncAction = Start | Cancel | Fulfill | Reject + +export declare interface AsyncOptions { + promise?: Promise + promiseFn?: PromiseFn + deferFn?: DeferFn + watch?: any + watchFn?: (props: AsyncProps, prevProps: AsyncProps) => any + initialValue?: T + onResolve?: (data: T) => void + onReject?: (error: Error) => void + reducer?: ( + state: AsyncState, + action: AsyncAction, + internalReducer: (state: AsyncState, action: AsyncAction) => AsyncState + ) => AsyncState + dispatcher?: ( + action: AsyncAction, + internalDispatch: (action: AsyncAction) => void, + props: AsyncProps + ) => void + debugLabel?: string + [prop: string]: any +} + +export declare interface AsyncProps extends AsyncOptions { + children?: AsyncChildren +} + +export interface AbstractState { + initialValue?: T | Error + counter: number + promise: Promise + run: (...args: any[]) => void + reload: () => void + cancel: () => void + setData: (data: T, callback?: () => void) => T + setError: (error: Error, callback?: () => void) => Error +} + +export declare type AsyncInitial> = S & { + initialValue?: undefined + data: undefined + error: undefined + value: undefined + startedAt: undefined + finishedAt: undefined + status: "initial" + isInitial: false + isPending: false + isLoading: false + isFulfilled: false + isResolved: false + isRejected: false + isSettled: false +} +export declare type AsyncPending> = S & { + data: T | undefined + error: Error | undefined + value: T | Error | undefined + startedAt: Date + finishedAt: undefined + status: "pending" + isInitial: false + isPending: true + isLoading: true + isFulfilled: false + isResolved: false + isRejected: false + isSettled: false +} +export declare type AsyncFulfilled> = S & { + data: T + error: undefined + value: T + startedAt: Date + finishedAt: Date + status: "fulfilled" + isInitial: false + isPending: false + isLoading: false + isFulfilled: true + isResolved: true + isRejected: false + isSettled: true +} +export declare type AsyncRejected> = S & { + data: T | undefined + error: Error + value: Error + startedAt: Date + finishedAt: Date + status: "rejected" + isInitial: false + isPending: false + isLoading: false + isFulfilled: false + isResolved: false + isRejected: true + isSettled: true +} +export declare type AsyncState = AbstractState> = + | AsyncInitial + | AsyncPending + | AsyncFulfilled + | AsyncRejected + +export declare class Async extends React.Component, AsyncState> {} + +interface AsyncConstructor extends ComponentClass> { + new (): Async + Initial: React.FC<{ children?: InitialChildren; persist?: boolean }> + Pending: React.FC<{ children?: PendingChildren; initial?: boolean }> + Loading: React.FC<{ children?: PendingChildren; initial?: boolean }> + Fulfilled: React.FC<{ children?: FulfilledChildren; persist?: boolean }> + Resolved: React.FC<{ children?: FulfilledChildren; persist?: boolean }> + Rejected: React.FC<{ children?: RejectedChildren; persist?: boolean }> + Settled: React.FC<{ children?: SettledChildren; persist?: boolean }> +} + /** * createInstance allows you to create instances of Async that are bound to a specific promise. * A unique instance also uses its own React context for better nesting capability. */ -export const createInstance = (defaultProps = {}, displayName = "Async") => { - const { Consumer, Provider } = React.createContext() +export const createInstance = ( + defaultProps: AsyncProps = {}, + displayName = "Async" +): AsyncConstructor => { + const { Consumer, Provider } = React.createContext>(undefined as any) + + type Props = AsyncProps + + const Async: AsyncConstructor = class extends React.Component> { + private mounted = false + private counter = 0 + private args: any[] = [] + private promise?: Promise = undefined + private abortController: AbortController = { abort: () => {} } as any + private debugLabel?: string + private dispatch: (action: AsyncAction, ...args: any[]) => void - class Async extends React.Component { - constructor(props) { + constructor(props: Props) { super(props) this.start = this.start.bind(this) @@ -29,13 +197,8 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { const promiseFn = props.promiseFn || defaultProps.promiseFn const initialValue = props.initialValue || defaultProps.initialValue - this.mounted = false - this.counter = 0 - this.args = [] - this.promise = undefined - this.abortController = { abort: () => {} } this.state = { - ...init({ initialValue, promise, promiseFn }), + ...init({ initialValue, promise, promiseFn }), cancel: this.cancel, run: this.run, reload: () => { @@ -50,10 +213,10 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { const { devToolsDispatcher } = globalScope.__REACT_ASYNC__ const _reducer = props.reducer || defaultProps.reducer const _dispatcher = props.dispatcher || defaultProps.dispatcher || devToolsDispatcher - const reducer = _reducer + const reducer: (state: AsyncState, action: AsyncAction) => AsyncState = _reducer ? (state, action) => _reducer(state, action, asyncReducer) : asyncReducer - const dispatch = dispatchMiddleware((action, callback) => { + const dispatch = dispatchMiddleware((action, callback) => { this.setState(state => reducer(state, action), callback) }) this.dispatch = _dispatcher ? action => _dispatcher(action, dispatch, props) : dispatch @@ -66,7 +229,7 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { } } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { const { watch, watchFn = defaultProps.watchFn, promise, promiseFn } = this.props if (watch !== prevProps.watch) { if (this.counter) this.cancel() @@ -94,7 +257,7 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { this.mounted = false } - getMeta(meta) { + getMeta(meta?: M) { return { counter: this.counter, promise: this.promise, @@ -103,10 +266,10 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { } } - start(promiseFn) { + start(promiseFn: () => Promise) { if ("AbortController" in globalScope) { this.abortController.abort() - this.abortController = new globalScope.AbortController() + this.abortController = new globalScope.AbortController!() } this.counter++ return (this.promise = new Promise((resolve, reject) => { @@ -131,7 +294,7 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { } } - run(...args) { + run(...args: any[]) { const deferFn = this.props.deferFn || defaultProps.deferFn if (deferFn) { this.args = args @@ -151,8 +314,8 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { this.mounted && this.dispatch({ type: actionTypes.cancel, meta: this.getMeta() }) } - onResolve(counter) { - return data => { + onResolve(counter: Number) { + return (data: T) => { if (this.counter === counter) { const onResolve = this.props.onResolve || defaultProps.onResolve this.setData(data, () => onResolve && onResolve(data)) @@ -161,8 +324,8 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { } } - onReject(counter) { - return error => { + onReject(counter: Number) { + return (error: Error) => { if (this.counter === counter) { const onReject = this.props.onReject || defaultProps.onReject this.setError(error, () => onReject && onReject(error)) @@ -171,13 +334,13 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { } } - setData(data, callback) { + setData(data: T, callback?: () => void) { this.mounted && this.dispatch({ type: actionTypes.fulfill, payload: data, meta: this.getMeta() }, callback) return data } - setError(error, callback) { + setError(error: Error, callback?: () => void) { this.mounted && this.dispatch( { type: actionTypes.reject, payload: error, error: true, meta: this.getMeta() }, @@ -189,22 +352,46 @@ export const createInstance = (defaultProps = {}, displayName = "Async") => { render() { const { children } = this.props if (typeof children === "function") { - return {children(this.state)} + return ( + + {(children as ((state: AsyncState) => React.ReactNode))(this.state)} + + ) } if (children !== undefined && children !== null) { return {children} } return null } - } + } as any - if (propTypes) Async.propTypes = propTypes.Async + if (propTypes) (Async as React.ComponentClass).propTypes = propTypes.Async - const AsyncInitial = props => {st => } - const AsyncPending = props => {st => } - const AsyncFulfilled = props => {st => } - const AsyncRejected = props => {st => } - const AsyncSettled = props => {st => } + const AsyncInitial: AsyncConstructor["Initial"] = props => ( + + {(st: AsyncState>) => } + + ) + const AsyncPending: AsyncConstructor["Pending"] = props => ( + + {(st: AsyncState>) => } + + ) + const AsyncFulfilled: AsyncConstructor["Fulfilled"] = props => ( + + {(st: AsyncState>) => } + + ) + const AsyncRejected: AsyncConstructor["Rejected"] = props => ( + + {(st: AsyncState>) => } + + ) + const AsyncSettled: AsyncConstructor["Settled"] = props => ( + + {(st: AsyncState>) => } + + ) AsyncInitial.displayName = `${displayName}.Initial` AsyncPending.displayName = `${displayName}.Pending` diff --git a/packages/react-async/src/globalScope.ts b/packages/react-async/src/globalScope.ts index 62316c15..2668b078 100644 --- a/packages/react-async/src/globalScope.ts +++ b/packages/react-async/src/globalScope.ts @@ -1,14 +1,20 @@ +import { any } from "prop-types" + /* istanbul ignore file */ /** * Universal global scope object. In the browser this is `self`, in Node.js and React Native it's `global`. * This file is excluded from coverage reporting because these globals are environment-specific so we can't test them all. */ -const globalScope = (() => { - if (typeof self === "object" && self.self === self) return self - if (typeof global === "object" && global.global === global) return global - if (typeof global === "object" && global.GLOBAL === global) return global - return {} // fallback that relies on imported modules to be singletons +const globalScope = ((): { + __REACT_ASYNC__?: any + AbortController?: typeof AbortController + fetch: Window["fetch"] +} => { + if (typeof self === "object" && self.self === self) return self as any + if (typeof global === "object" && global.global === global) return global as any + if (typeof global === "object" && global.GLOBAL === global) return global as any + return {} as any // fallback that relies on imported modules to be singletons })() /** diff --git a/packages/react-async/src/helpers.tsx b/packages/react-async/src/helpers.tsx index 09232f99..1c1acdc8 100644 --- a/packages/react-async/src/helpers.tsx +++ b/packages/react-async/src/helpers.tsx @@ -1,8 +1,34 @@ +import React from "react" import propTypes from "./propTypes" -const nullify = children => (children === undefined ? null : children) -const renderFn = (children, ...args) => - nullify(typeof children === "function" ? children(...args) : children) +import { + InitialChildren, + PendingChildren, + FulfilledChildren, + RejectedChildren, + SettledChildren, + AsyncState, +} from "./Async" + +// these were exported as type, but never existed +// export declare function IfLoading(props: { +// children?: PendingChildren +// initial?: boolean +// state: AsyncState +// }): React.ReactNode +// export declare function IfResolved(props: { +// children?: FulfilledChildren +// persist?: boolean +// state: AsyncState +// }): React.ReactNode + +const nullify = (children: T | undefined): T | null => + children === undefined ? null : children + +const renderFn = ( + children: React.ReactNode | ((...args: any[]) => React.ReactNode), + ...args: any[] +) => nullify(typeof children === "function" ? (children as any)(...args) : children) /** * Renders only when no promise has started or completed yet. @@ -11,8 +37,15 @@ const renderFn = (children, ...args) => * @prop {Object} state React Async state object * @prop {boolean} persist Show until we have data, even while pending (loading) or when an error occurred */ -export const IfInitial = ({ children, persist, state = {} }) => - state.isInitial || (persist && !state.data) ? renderFn(children, state) : null +export const IfInitial = ({ + children, + persist, + state = {} as any, +}: { + children?: InitialChildren + persist?: boolean + state: AsyncState +}) => <>{state.isInitial || (persist && !state.data) ? renderFn(children, state) : null} /** * Renders only while pending (promise is loading). @@ -21,8 +54,15 @@ export const IfInitial = ({ children, persist, state = {} }) => * @prop {Object} state React Async state object * @prop {boolean} initial Show only on initial load (data is undefined) */ -export const IfPending = ({ children, initial, state = {} }) => - state.isPending && (!initial || !state.value) ? renderFn(children, state) : null +export const IfPending = ({ + children, + initial, + state = {} as any, +}: { + children?: PendingChildren + initial?: boolean + state: AsyncState +}) => <>{state.isPending && (!initial || !state.value) ? renderFn(children, state) : null} /** * Renders only when promise is resolved. @@ -31,8 +71,17 @@ export const IfPending = ({ children, initial, state = {} }) => * @prop {Object} state React Async state object * @prop {boolean} persist Show old data while pending (promise is loading) */ -export const IfFulfilled = ({ children, persist, state = {} }) => - state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null +export const IfFulfilled = ({ + children, + persist, + state = {} as any, +}: { + children?: FulfilledChildren + persist?: boolean + state: AsyncState +}) => ( + <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} +) /** * Renders only when promise is rejected. @@ -41,8 +90,19 @@ export const IfFulfilled = ({ children, persist, state = {} }) => * @prop {Object} state React Async state object * @prop {boolean} persist Show old error while pending (promise is loading) */ -export const IfRejected = ({ children, persist, state = {} }) => - state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null +export const IfRejected = ({ + children, + persist, + state = {} as any, +}: { + children?: RejectedChildren + persist?: boolean + state: AsyncState +}) => ( + <> + {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} + +) /** * Renders only when promise is fulfilled or rejected. @@ -51,8 +111,15 @@ export const IfRejected = ({ children, persist, state = {} }) => * @prop {Object} state React Async state object * @prop {boolean} persist Show old data or error while pending (promise is loading) */ -export const IfSettled = ({ children, persist, state = {} }) => - state.isSettled || (persist && state.value) ? renderFn(children, state) : null +export const IfSettled = ({ + children, + persist, + state = {} as any, +}: { + children?: SettledChildren + persist?: boolean + state: AsyncState +}) => <>{state.isSettled || (persist && state.value) ? renderFn(children, state) : null} if (propTypes) { IfInitial.propTypes = propTypes.Initial diff --git a/packages/react-async/src/reducer.ts b/packages/react-async/src/reducer.ts index 27bf65a2..13b63fa2 100644 --- a/packages/react-async/src/reducer.ts +++ b/packages/react-async/src/reducer.ts @@ -1,13 +1,30 @@ import { getInitialStatus, getIdleStatus, getStatusProps, statusTypes } from "./status" +import { + PromiseFn, + AsyncState, + AsyncAction, + AsyncPending, + AsyncFulfilled, + AsyncRejected, + AsyncInitial, +} from "./Async" -export const actionTypes = { - start: "start", - cancel: "cancel", - fulfill: "fulfill", - reject: "reject", +export enum actionTypes { + start = "start", + cancel = "cancel", + fulfill = "fulfill", + reject = "reject", } -export const init = ({ initialValue, promise, promiseFn }) => ({ +export const init = ({ + initialValue, + promise, + promiseFn, +}: { + initialValue?: Error | T + promise?: Promise + promiseFn?: PromiseFn +}): AsyncState => ({ initialValue, data: initialValue instanceof Error ? undefined : initialValue, error: initialValue instanceof Error ? initialValue : undefined, @@ -16,54 +33,57 @@ export const init = ({ initialValue, promise, promiseFn }) => ({ finishedAt: initialValue ? new Date() : undefined, ...getStatusProps(getInitialStatus(initialValue, promise || promiseFn)), counter: 0, + // @ts-ignore see #92 promise: undefined, }) -export const reducer = (state, { type, payload, meta }) => { - switch (type) { +export const reducer = (state: AsyncState, action: AsyncAction) => { + switch (action.type) { case actionTypes.start: return { ...state, startedAt: new Date(), finishedAt: undefined, ...getStatusProps(statusTypes.pending), - counter: meta.counter, - promise: meta.promise, - } + counter: action.meta.counter, + promise: action.meta.promise, + } as AsyncPending case actionTypes.cancel: return { ...state, startedAt: undefined, finishedAt: undefined, ...getStatusProps(getIdleStatus(state.error || state.data)), - counter: meta.counter, - promise: meta.promise, - } + counter: action.meta.counter, + promise: action.meta.promise, + } as AsyncInitial | AsyncFulfilled | AsyncRejected case actionTypes.fulfill: return { ...state, - data: payload, - value: payload, + data: action.payload, + value: action.payload, error: undefined, finishedAt: new Date(), ...getStatusProps(statusTypes.fulfilled), - promise: meta.promise, - } + promise: action.meta.promise, + } as AsyncFulfilled case actionTypes.reject: return { ...state, - error: payload, - value: payload, + error: action.payload, + value: action.payload, finishedAt: new Date(), ...getStatusProps(statusTypes.rejected), - promise: meta.promise, - } + promise: action.meta.promise, + } as AsyncRejected default: return state } } -export const dispatchMiddleware = dispatch => (action, ...args) => { +export const dispatchMiddleware = ( + dispatch: (action: AsyncAction, ...args: any[]) => void +) => (action: AsyncAction, ...args: unknown[]) => { dispatch(action, ...args) if (action.type === actionTypes.start && typeof action.payload === "function") { action.payload() diff --git a/packages/react-async/src/status.ts b/packages/react-async/src/status.ts index 0af8fd52..34b6df7f 100644 --- a/packages/react-async/src/status.ts +++ b/packages/react-async/src/status.ts @@ -1,24 +1,26 @@ -export const statusTypes = { - initial: "initial", - pending: "pending", - fulfilled: "fulfilled", - rejected: "rejected", +import { PromiseFn } from "./Async" + +export enum statusTypes { + initial = "initial", + pending = "pending", + fulfilled = "fulfilled", + rejected = "rejected", } -export const getInitialStatus = (value, promise) => { +export const getInitialStatus = (value?: T | Error, promise?: Promise | PromiseFn) => { if (value instanceof Error) return statusTypes.rejected if (value !== undefined) return statusTypes.fulfilled if (promise) return statusTypes.pending return statusTypes.initial } -export const getIdleStatus = value => { +export const getIdleStatus = (value?: T | Error) => { if (value instanceof Error) return statusTypes.rejected if (value !== undefined) return statusTypes.fulfilled return statusTypes.initial } -export const getStatusProps = status => ({ +export const getStatusProps = (status: statusTypes) => ({ status, isInitial: status === statusTypes.initial, isPending: status === statusTypes.pending, diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index b2c14509..8273da2c 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -3,17 +3,38 @@ import { useCallback, useDebugValue, useEffect, useMemo, useRef, useReducer } fr import globalScope from "./globalScope" import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" +import { AsyncOptions, AsyncState, AbstractState, PromiseFn, Meta } from "./Async" + +export interface FetchOptions extends AsyncOptions { + defer?: boolean + json?: boolean +} + const noop = () => {} -const useAsync = (arg1, arg2) => { - const options = typeof arg1 === "function" ? { ...arg2, promiseFn: arg1 } : arg1 +function useAsync(options: AsyncOptions): AsyncState +function useAsync(promiseFn: PromiseFn, options?: AsyncOptions): AsyncState + +function useAsync( + arg1: AsyncOptions | PromiseFn, + arg2?: AsyncOptions +): AsyncState { + const options: AsyncOptions = + typeof arg1 === "function" + ? { + ...arg2, + promiseFn: arg1, + } + : arg1 const counter = useRef(0) const isMounted = useRef(true) - const lastArgs = useRef(undefined) - const lastOptions = useRef(undefined) - const lastPromise = useRef(undefined) - const abortController = useRef({ abort: noop }) + const lastArgs = useRef(undefined) + const lastOptions = useRef | undefined>(undefined) + const lastPromise = useRef | undefined>(undefined) + const abortController = useRef({ + abort: noop, + } as any) const { devToolsDispatcher } = globalScope.__REACT_ASYNC__ const { reducer, dispatcher = devToolsDispatcher } = options @@ -30,15 +51,25 @@ const useAsync = (arg1, arg2) => { ) const { debugLabel } = options - const getMeta = useCallback( - meta => ({ counter: counter.current, promise: lastPromise.current, debugLabel, ...meta }), + const getMeta: (meta?: M) => M = useCallback( + (meta?) => + ({ + counter: counter.current, + promise: lastPromise.current, + debugLabel, + ...meta, + } as any), [debugLabel] ) const setData = useCallback( (data, callback = noop) => { if (isMounted.current) { - dispatch({ type: actionTypes.fulfill, payload: data, meta: getMeta() }) + dispatch({ + type: actionTypes.fulfill, + payload: data, + meta: getMeta(), + }) callback() } return data @@ -49,7 +80,12 @@ const useAsync = (arg1, arg2) => { const setError = useCallback( (error, callback = noop) => { if (isMounted.current) { - dispatch({ type: actionTypes.reject, payload: error, error: true, meta: getMeta() }) + dispatch({ + type: actionTypes.reject, + payload: error, + error: true, + meta: getMeta(), + }) callback() } return error @@ -59,11 +95,13 @@ const useAsync = (arg1, arg2) => { const { onResolve, onReject } = options const handleResolve = useCallback( - count => data => count === counter.current && setData(data, () => onResolve && onResolve(data)), + count => (data: T) => + count === counter.current && setData(data, () => onResolve && onResolve(data)), [setData, onResolve] ) const handleReject = useCallback( - count => err => count === counter.current && setError(err, () => onReject && onReject(err)), + count => (err: Error) => + count === counter.current && setError(err, () => onReject && onReject(err)), [setError, onReject] ) @@ -71,13 +109,17 @@ const useAsync = (arg1, arg2) => { promiseFn => { if ("AbortController" in globalScope) { abortController.current.abort() - abortController.current = new globalScope.AbortController() + abortController.current = new globalScope.AbortController!() } counter.current++ return (lastPromise.current = new Promise((resolve, reject) => { if (!isMounted.current) return const executor = () => promiseFn().then(resolve, reject) - dispatch({ type: actionTypes.start, payload: executor, meta: getMeta() }) + dispatch({ + type: actionTypes.start, + payload: executor, + meta: getMeta(), + }) })) }, [dispatch, getMeta] @@ -91,7 +133,7 @@ const useAsync = (arg1, arg2) => { .then(handleResolve(counter.current)) .catch(handleReject(counter.current)) } else if (promiseFn && !isPreInitialized) { - start(() => promiseFn(lastOptions.current, abortController.current)) + start(() => promiseFn(lastOptions.current!, abortController.current)) .then(handleResolve(counter.current)) .catch(handleReject(counter.current)) } @@ -102,7 +144,7 @@ const useAsync = (arg1, arg2) => { (...args) => { if (deferFn) { lastArgs.current = args - start(() => deferFn(args, lastOptions.current, abortController.current)) + start(() => deferFn(args, lastOptions.current!, abortController.current)) .then(handleResolve(counter.current)) .catch(handleReject(counter.current)) } @@ -119,7 +161,11 @@ const useAsync = (arg1, arg2) => { onCancel && onCancel() counter.current++ abortController.current.abort() - isMounted.current && dispatch({ type: actionTypes.cancel, meta: getMeta() }) + isMounted.current && + dispatch({ + type: actionTypes.cancel, + meta: getMeta(), + }) }, [onCancel, dispatch, getMeta]) /* These effects should only be triggered on changes to specific props */ @@ -135,52 +181,97 @@ const useAsync = (arg1, arg2) => { if (counter.current) cancel() if (promise || promiseFn) load() }, [promise, promiseFn, watch]) - useEffect(() => () => (isMounted.current = false), []) + useEffect( + () => () => { + isMounted.current = false + }, + [] + ) useEffect(() => () => cancel(), []) /* eslint-enable react-hooks/exhaustive-deps */ useDebugValue(state, ({ status }) => `[${counter.current}] ${status}`) return useMemo( - () => ({ - ...state, - run, - reload, - cancel, - setData, - setError, - }), + () => + ({ + ...state, + run, + reload, + cancel, + setData, + setError, + } as AsyncState), [state, run, reload, cancel, setData, setError] ) } -const parseResponse = (accept, json) => res => { +const parseResponse = (accept: undefined | string, json: undefined | boolean) => ( + res: Response +) => { if (!res.ok) return Promise.reject(res) if (typeof json === "boolean") return json ? res.json() : res return accept === "application/json" ? res.json() : res } -const useAsyncFetch = (input, init, { defer, json, ...options } = {}) => { - const method = input.method || (init && init.method) - const headers = input.headers || (init && init.headers) || {} - const accept = headers["Accept"] || headers["accept"] || (headers.get && headers.get("accept")) - const doFetch = (input, init) => globalScope.fetch(input, init).then(parseResponse(accept, json)) +interface FetchRun extends Omit, "run"> { + run(overrideInit: (init: RequestInit) => RequestInit): void + run(overrideInit: Partial): void + run(ignoredEvent: React.SyntheticEvent): void + run(ignoredEvent: Event): void + run(): void +} + +/** + * + * @param {RequestInfo} input + * @param {RequestInit} init + * @param {FetchOptions} options + * @returns {AsyncState>} + */ +const useAsyncFetch = ( + input: RequestInfo, + init: RequestInit, + { defer, json, ...options }: FetchOptions = {} +): AsyncState> => { + const method = (input as Request).method || (init && init.method) + const headers: Headers & Record = + (input as Request).headers || (init && init.headers) || {} + const accept: string | undefined = + headers["Accept"] || headers["accept"] || (headers.get && headers.get("accept")) + const doFetch = (input: RequestInfo, init: RequestInit) => + globalScope.fetch(input, init).then(parseResponse(accept, json)) const isDefer = - typeof defer === "boolean" ? defer : ["POST", "PUT", "PATCH", "DELETE"].indexOf(method) !== -1 + typeof defer === "boolean" ? defer : ["POST", "PUT", "PATCH", "DELETE"].indexOf(method!) !== -1 const fn = isDefer ? "deferFn" : "promiseFn" - const identity = JSON.stringify({ input, init, isDefer }) + const identity = JSON.stringify({ + input, + init, + isDefer, + }) const state = useAsync({ ...options, [fn]: useCallback( - (arg1, arg2, arg3) => { + // TODO + (arg1: any, arg2: any, arg3: any) => { const [override, signal] = arg3 ? [arg1[0], arg3.signal] : [undefined, arg2.signal] if (typeof override === "object" && "preventDefault" in override) { // Don't spread Events or SyntheticEvents - return doFetch(input, { signal, ...init }) + return doFetch(input, { + signal, + ...init, + }) } return typeof override === "function" - ? doFetch(input, { signal, ...override(init) }) - : doFetch(input, { signal, ...init, ...override }) + ? doFetch(input, { + signal, + ...override(init), + }) + : doFetch(input, { + signal, + ...init, + ...override, + }) }, [identity] // eslint-disable-line react-hooks/exhaustive-deps ), diff --git a/packages/react-async/tsconfig.json b/packages/react-async/tsconfig.json new file mode 100644 index 00000000..54bd4795 --- /dev/null +++ b/packages/react-async/tsconfig.json @@ -0,0 +1,70 @@ +{ + "compilerOptions": { + /* Basic Options */ + "target": "es2019", + /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ + "module": "esnext", + /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": false, + /* Allow javascript files to be compiled. */ + "checkJs": false, + /* Report errors in .js files. */ + "jsx": "react", + /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + "declaration": true, + /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "outDir": "./", /* Redirect output structure to the directory. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "incremental": true, /* Enable incremental compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + "skipLibCheck": true, + /* Strict Type-Checking Options */ + "strict": true, + /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + }, + "include": ["src"] +} \ No newline at end of file diff --git a/packages/react-async/yarn.lock b/packages/react-async/yarn.lock new file mode 100644 index 00000000..8dfa8fd0 --- /dev/null +++ b/packages/react-async/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +typescript@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54" + integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw== diff --git a/yarn.lock b/yarn.lock index ca206291..43cf4156 100644 --- a/yarn.lock +++ b/yarn.lock @@ -350,7 +350,7 @@ "@babel/helper-create-class-features-plugin" "^7.4.4" "@babel/helper-plugin-utils" "^7.0.0" -"@babel/plugin-proposal-class-properties@7.5.5", "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.3.3": +"@babel/plugin-proposal-class-properties@7.5.5", "@babel/plugin-proposal-class-properties@^7.0.0", "@babel/plugin-proposal-class-properties@^7.3.3", "@babel/plugin-proposal-class-properties@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.5.5.tgz#a974cfae1e37c3110e71f3c6a2e48b8e71958cd4" integrity sha512-AF79FsnWFxjlaosgdi421vmYG6/jg79bVD0dpD44QdgobzHKuLZ6S3vl8la9qIeSwGi8i1fS0O1mfuDAAdo1/A== @@ -1072,7 +1072,7 @@ "@babel/plugin-transform-react-jsx-self" "^7.0.0" "@babel/plugin-transform-react-jsx-source" "^7.0.0" -"@babel/preset-typescript@7.3.3", "@babel/preset-typescript@^7.1.0": +"@babel/preset-typescript@7.3.3", "@babel/preset-typescript@^7.1.0", "@babel/preset-typescript@^7.3.3": version "7.3.3" resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a" integrity sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg== @@ -2465,6 +2465,14 @@ "@types/node" "^10.12.18" standard-pkg "^0.4.0" +"@pika/plugin-ts-standard-pkg@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@pika/plugin-ts-standard-pkg/-/plugin-ts-standard-pkg-0.6.0.tgz#cf8fd7fa8a15709399e9cd61a0bd1c787365374c" + integrity sha512-R28JeAboHP9QrSZBijZGsbyOYBnGkRmNRWMjxqS81RugXLvBuivfLtaBVQWclN+c3lP6PDZFYpe6qRHIWvdTaQ== + dependencies: + execa "^2.0.0" + standard-pkg "^0.4.0" + "@pika/types@^0.6.0": version "0.6.0" resolved "https://registry.yarnpkg.com/@pika/types/-/types-0.6.0.tgz#eea0ed7db8c2593148a2e2b1bf4cfc25092a78dc" @@ -17630,6 +17638,11 @@ typescript@^3.5.1: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== +typescript@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54" + integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw== + typescript@~3.4.0: version "3.4.5" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" From 797ef8603ab1fbc187a89b8f6bdd7e606e92ce95 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 6 Sep 2019 17:04:30 +0200 Subject: [PATCH 03/47] add linting for TS --- .eslintrc | 16 +++++++++-- package.json | 4 ++- packages/react-async/src/Async.tsx | 2 +- yarn.lock | 44 ++++++++++++++++++++++++++++-- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/.eslintrc b/.eslintrc index 70843243..02a3ac38 100644 --- a/.eslintrc +++ b/.eslintrc @@ -20,5 +20,17 @@ "react": { "version": "detect" } - } -} + }, + "overrides": [{ + "files": "packages/**/*.{ts,tsx}", + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module", + "ecmaFeatures": { + "modules": true + } + } + }] +} \ No newline at end of file diff --git a/package.json b/package.json index 98ab0d14..664a4f2d 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "start": "run-p start:*", "start:examples": "now dev", "start:storybook": "start-storybook -p 6006", - "lint": "eslint packages/*/src/*.js", + "lint": "eslint packages/*/src/*.{j,t}{s,sx}", "test": "jest packages/*/src/*.spec.js", "test:watch": "yarn test -- --watch", "test:devtools": "jest react-async-devtools/src", @@ -51,6 +51,8 @@ "@storybook/react": "5.2.0-rc.2", "@testing-library/jest-dom": "4.1.0", "@testing-library/react": "9.1.3", + "@typescript-eslint/eslint-plugin": "^2.1.0", + "@typescript-eslint/parser": "^2.1.0", "babel-eslint": "10.0.3", "babel-jest": "24.9.0", "babel-loader": "8.0.6", diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index 0215b2b0..7291c64a 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -172,7 +172,7 @@ export const createInstance = ( type Props = AsyncProps - const Async: AsyncConstructor = class extends React.Component> { + const Async: AsyncConstructor = class Async extends React.Component> { private mounted = false private counter = 0 private args: any[] = [] diff --git a/yarn.lock b/yarn.lock index 43cf4156..b2c716cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3281,6 +3281,17 @@ regexpp "^2.0.1" tsutils "^3.7.0" +"@typescript-eslint/eslint-plugin@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.1.0.tgz#4bcd978d88419ea971613675f2620dde39920d69" + integrity sha512-3i/dLPwxaVfCsaLu3HkB8CAA1Uw3McAegrTs+VBJ0BrGRKW7nUwSqRfHfCS7sw7zSbf62q3v0v6pOS8MyaYItg== + dependencies: + "@typescript-eslint/experimental-utils" "2.1.0" + eslint-utils "^1.4.0" + functional-red-black-tree "^1.0.1" + regexpp "^2.0.1" + tsutils "^3.14.0" + "@typescript-eslint/experimental-utils@1.13.0", "@typescript-eslint/experimental-utils@^1.13.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-1.13.0.tgz#b08c60d780c0067de2fb44b04b432f540138301e" @@ -3290,6 +3301,15 @@ "@typescript-eslint/typescript-estree" "1.13.0" eslint-scope "^4.0.0" +"@typescript-eslint/experimental-utils@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.1.0.tgz#0837229f0e75a32db0db9bf662ad0eface914453" + integrity sha512-ZJGLYXa4nxjNzomaEk1qts38B/vludg2LOM7dRc7SppEKsMPTS1swaTKS/pom+x4d/luJGoG00BDIss7PR1NQA== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.1.0" + eslint-scope "^4.0.0" + "@typescript-eslint/parser@1.13.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.13.0.tgz#61ac7811ea52791c47dc9fd4dd4a184fae9ac355" @@ -3300,6 +3320,16 @@ "@typescript-eslint/typescript-estree" "1.13.0" eslint-visitor-keys "^1.0.0" +"@typescript-eslint/parser@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.1.0.tgz#ca62b26fa6a5a34ecdec4a000f22baf103791830" + integrity sha512-0+hzirRJoqE1T4lSSvCfKD+kWjIpDWfbGBiisK5CENcr+22pPkHB2sfV1giON+UxHV4A08SSrQonZk7X2zIQdw== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.1.0" + "@typescript-eslint/typescript-estree" "2.1.0" + eslint-visitor-keys "^1.0.0" + "@typescript-eslint/typescript-estree@1.13.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.13.0.tgz#8140f17d0f60c03619798f1d628b8434913dc32e" @@ -3308,6 +3338,16 @@ lodash.unescape "4.0.1" semver "5.5.0" +"@typescript-eslint/typescript-estree@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.1.0.tgz#88e676cc9760516711f6fe43958adc31b93de8e5" + integrity sha512-482ErJJ7QYghBh+KA9G+Fwcuk/PLTy+9NBMz8S+6UFrUUnVvHRNAL7I70kdws2te0FBYEZW7pkDaXoT+y8UARw== + dependencies: + glob "^7.1.4" + is-glob "^4.0.1" + lodash.unescape "4.0.1" + semver "^6.2.0" + "@unimodules/core@~3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@unimodules/core/-/core-3.0.2.tgz#a2b143fb1e743809ba17c60ae1848f82b8637901" @@ -7305,7 +7345,7 @@ eslint-scope@^5.0.0: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-utils@^1.3.1, eslint-utils@^1.4.2: +eslint-utils@^1.3.1, eslint-utils@^1.4.0, eslint-utils@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== @@ -17557,7 +17597,7 @@ tslib@^1.8.1, tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -tsutils@^3.7.0: +tsutils@^3.14.0, tsutils@^3.7.0: version "3.17.1" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== From c22687b43c275d0e045f23ec1f4ac8c10731462c Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 6 Sep 2019 17:09:45 +0200 Subject: [PATCH 04/47] remove old types --- packages/react-async/src/globalScope.ts | 2 - packages/react-async/src/index.d.ts | 246 ------------------------ 2 files changed, 248 deletions(-) delete mode 100644 packages/react-async/src/index.d.ts diff --git a/packages/react-async/src/globalScope.ts b/packages/react-async/src/globalScope.ts index 2668b078..e1fa63ac 100644 --- a/packages/react-async/src/globalScope.ts +++ b/packages/react-async/src/globalScope.ts @@ -1,5 +1,3 @@ -import { any } from "prop-types" - /* istanbul ignore file */ /** diff --git a/packages/react-async/src/index.d.ts b/packages/react-async/src/index.d.ts deleted file mode 100644 index 70f16106..00000000 --- a/packages/react-async/src/index.d.ts +++ /dev/null @@ -1,246 +0,0 @@ -import React from "react" - -export type AsyncChildren = ((state: AsyncState) => React.ReactNode) | React.ReactNode -export type InitialChildren = ((state: AsyncInitial) => React.ReactNode) | React.ReactNode -export type PendingChildren = ((state: AsyncPending) => React.ReactNode) | React.ReactNode -export type FulfilledChildren = - | ((data: T, state: AsyncFulfilled) => React.ReactNode) - | React.ReactNode -export type RejectedChildren = - | ((error: Error, state: AsyncRejected) => React.ReactNode) - | React.ReactNode -export type SettledChildren = - | ((state: AsyncFulfilled | AsyncRejected) => React.ReactNode) - | React.ReactNode - -export type PromiseFn = (props: AsyncProps, controller: AbortController) => Promise -export type DeferFn = ( - args: any[], - props: AsyncProps, - controller: AbortController -) => Promise - -interface AbstractAction { - type: string - meta: { counter: number; [meta: string]: any } -} -export type Start = AbstractAction & { type: "start"; payload: () => Promise } -export type Cancel = AbstractAction & { type: "cancel" } -export type Fulfill = AbstractAction & { type: "fulfill"; payload: T } -export type Reject = AbstractAction & { type: "reject"; payload: Error; error: true } -export type AsyncAction = Start | Cancel | Fulfill | Reject - -export interface AsyncOptions { - promise?: Promise - promiseFn?: PromiseFn - deferFn?: DeferFn - watch?: any - watchFn?: (props: AsyncProps, prevProps: AsyncProps) => any - initialValue?: T - onResolve?: (data: T) => void - onReject?: (error: Error) => void - reducer?: ( - state: AsyncState, - action: AsyncAction, - internalReducer: (state: AsyncState, action: AsyncAction) => AsyncState - ) => AsyncState - dispatcher?: ( - action: AsyncAction, - internalDispatch: (action: AsyncAction) => void, - props: AsyncProps - ) => void - debugLabel?: string - [prop: string]: any -} - -export interface AsyncProps extends AsyncOptions { - children?: AsyncChildren -} - -interface AbstractState { - initialValue?: T | Error - counter: number - promise: Promise - run: (...args: any[]) => void - reload: () => void - cancel: () => void - setData: (data: T, callback?: () => void) => T - setError: (error: Error, callback?: () => void) => Error -} - -export type AsyncInitial = AbstractState & { - initialValue?: undefined - data: undefined - error: undefined - value: undefined - startedAt: undefined - finishedAt: undefined - status: "initial" - isInitial: false - isPending: false - isLoading: false - isFulfilled: false - isResolved: false - isRejected: false - isSettled: false -} -export type AsyncPending = AbstractState & { - data: T | undefined - error: Error | undefined - value: T | Error | undefined - startedAt: Date - finishedAt: undefined - status: "pending" - isInitial: false - isPending: true - isLoading: true - isFulfilled: false - isResolved: false - isRejected: false - isSettled: false -} -export type AsyncFulfilled = AbstractState & { - data: T - error: undefined - value: T - startedAt: Date - finishedAt: Date - status: "fulfilled" - isInitial: false - isPending: false - isLoading: false - isFulfilled: true - isResolved: true - isRejected: false - isSettled: true -} -export type AsyncRejected = AbstractState & { - data: T | undefined - error: Error - value: Error - startedAt: Date - finishedAt: Date - status: "rejected" - isInitial: false - isPending: false - isLoading: false - isFulfilled: false - isResolved: false - isRejected: true - isSettled: true -} -export type AsyncState = AsyncInitial | AsyncPending | AsyncFulfilled | AsyncRejected - -export class Async extends React.Component, AsyncState> {} - -export namespace Async { - export function Initial(props: { - children?: InitialChildren - persist?: boolean - }): JSX.Element - export function Pending(props: { - children?: PendingChildren - initial?: boolean - }): JSX.Element - export function Loading(props: { - children?: PendingChildren - initial?: boolean - }): JSX.Element - export function Fulfilled(props: { - children?: FulfilledChildren - persist?: boolean - }): JSX.Element - export function Resolved(props: { - children?: FulfilledChildren - persist?: boolean - }): JSX.Element - export function Rejected(props: { - children?: RejectedChildren - persist?: boolean - }): JSX.Element - export function Settled(props: { - children?: SettledChildren - persist?: boolean - }): JSX.Element -} - -export function createInstance( - defaultProps?: AsyncProps -): (new () => Async) & { - Initial(props: { children?: InitialChildren; persist?: boolean }): JSX.Element - Pending(props: { children?: PendingChildren; initial?: boolean }): JSX.Element - Loading(props: { children?: PendingChildren; initial?: boolean }): JSX.Element - Fulfilled(props: { children?: FulfilledChildren; persist?: boolean }): JSX.Element - Resolved(props: { children?: FulfilledChildren; persist?: boolean }): JSX.Element - Rejected(props: { children?: RejectedChildren; persist?: boolean }): JSX.Element - Settled(props: { children?: SettledChildren; persist?: boolean }): JSX.Element -} - -export function IfInitial(props: { - children?: InitialChildren - persist?: boolean - state: AsyncState -}): JSX.Element -export function IfPending(props: { - children?: PendingChildren - initial?: boolean - state: AsyncState -}): JSX.Element -export function IfLoading(props: { - children?: PendingChildren - initial?: boolean - state: AsyncState -}): JSX.Element -export function IfFulfilled(props: { - children?: FulfilledChildren - persist?: boolean - state: AsyncState -}): JSX.Element -export function IfResolved(props: { - children?: FulfilledChildren - persist?: boolean - state: AsyncState -}): JSX.Element -export function IfRejected(props: { - children?: RejectedChildren - persist?: boolean - state: AsyncState -}): JSX.Element -export function IfSettled(props: { - children?: SettledChildren - persist?: boolean - state: AsyncState -}): JSX.Element - -export function useAsync( - arg1: AsyncOptions | PromiseFn, - arg2?: AsyncOptions -): AsyncState - -export interface FetchOptions extends AsyncOptions { - defer?: boolean - json?: boolean -} - -export function useFetch( - input: RequestInfo, - init?: RequestInit, - options?: FetchOptions -): AsyncInitialWithout<"run", T> & FetchRun - -// unfortunately, we cannot just omit K from AsyncInitial as that would unbox the Discriminated Union -type AsyncInitialWithout, T> = - | Omit, K> - | Omit, K> - | Omit, K> - | Omit, K> - -type FetchRun = { - run(overrideInit: (init: RequestInit) => RequestInit): void - run(overrideInit: Partial): void - run(ignoredEvent: React.SyntheticEvent): void - run(ignoredEvent: Event): void - run(): void -} - -export default Async From 5c9d14617227fc2b786b98eafc0c1087ce7efeff Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 6 Sep 2019 22:49:05 +0200 Subject: [PATCH 05/47] fix storybook --- .storybook/webpack.config.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js index 3976b3d1..3f6fe527 100644 --- a/.storybook/webpack.config.js +++ b/.storybook/webpack.config.js @@ -1,4 +1,12 @@ module.exports = async ({ config }) => { delete config.module.rules[0].include + config.module.rules.push({ + test: /\.(ts|tsx)$/, + loader: require.resolve('babel-loader'), + options: { + presets: [['react-app', { flow: false, typescript: true }]], + }, + }); + config.resolve.extensions.push('.ts', '.tsx'); return config } From 0321c69f1bc4df61f89fa127045fbbb24add087f Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Thu, 12 Sep 2019 08:38:53 +0200 Subject: [PATCH 06/47] add @pika/plugin-bundle-types --- package.json | 3 +- packages/react-async/package.json | 3 + yarn.lock | 140 ++++++++++++++++++++++++++++-- 3 files changed, 138 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 6256098a..296428aa 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@pika/plugin-build-node": "0.6.0", "@pika/plugin-build-types": "0.6.0", "@pika/plugin-build-web": "0.6.0", + "@pika/plugin-bundle-types": "^0.6.0", "@pika/plugin-standard-pkg": "0.6.0", "@pika/plugin-ts-standard-pkg": "^0.6.0", "@storybook/react": "5.2.0-rc.2", @@ -75,4 +76,4 @@ "react-dom": "16.9.0", "storybook-chromatic": "2.2.2" } -} \ No newline at end of file +} diff --git a/packages/react-async/package.json b/packages/react-async/package.json index a6824eb4..f3fcb20c 100644 --- a/packages/react-async/package.json +++ b/packages/react-async/package.json @@ -36,6 +36,9 @@ ], [ "@pika/plugin-build-web" + ], + [ + "@pika/plugin-bundle-types" ] ] }, diff --git a/yarn.lock b/yarn.lock index 24fa6b57..16600a62 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2453,6 +2453,59 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" +"@microsoft/api-extractor-model@7.4.1": + version "7.4.1" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor-model/-/api-extractor-model-7.4.1.tgz#3376f72570d336960c9b7b0dd44c8a0dbbe34604" + integrity sha512-rBO/QbrOMCdL8e9qwhIu1aH4C5sKOnUO1YhEh3+kVieFzTjiRnync7ghyQOtCaCVl2VXtp4LuOIv02e82oRqUg== + dependencies: + "@microsoft/node-core-library" "3.14.2" + "@microsoft/tsdoc" "0.12.14" + "@types/node" "8.5.8" + +"@microsoft/api-extractor@^7.1.5": + version "7.4.2" + resolved "https://registry.yarnpkg.com/@microsoft/api-extractor/-/api-extractor-7.4.2.tgz#440023cf05c69840e054cdb5f85cab9680227a40" + integrity sha512-O8OEaFvsvWEuwkOcVyWegIAFDY6TBZBvSIoOKLsSQYiQZtryGf13e2ym83iewhbUN7RmuOJtyQUKlBvcJbpgQA== + dependencies: + "@microsoft/api-extractor-model" "7.4.1" + "@microsoft/node-core-library" "3.14.2" + "@microsoft/ts-command-line" "4.2.8" + "@microsoft/tsdoc" "0.12.14" + colors "~1.2.1" + lodash "~4.17.15" + resolve "1.8.1" + source-map "~0.6.1" + typescript "~3.5.3" + +"@microsoft/node-core-library@3.14.2": + version "3.14.2" + resolved "https://registry.yarnpkg.com/@microsoft/node-core-library/-/node-core-library-3.14.2.tgz#255d421963f2d447a19f935e3c8eb3053e8e381b" + integrity sha512-bd8XhqhIvXsWg/SSNsZJdJxkN8Ucj7XKQkRe4cdYiKqpVdAREvW/shw8AoZIdgvjLI53029I/MO2Wn/AjGD3Jw== + dependencies: + "@types/fs-extra" "5.0.4" + "@types/jju" "~1.4.0" + "@types/node" "8.5.8" + "@types/z-schema" "3.16.31" + colors "~1.2.1" + fs-extra "~7.0.1" + jju "~1.4.0" + z-schema "~3.18.3" + +"@microsoft/ts-command-line@4.2.8": + version "4.2.8" + resolved "https://registry.yarnpkg.com/@microsoft/ts-command-line/-/ts-command-line-4.2.8.tgz#92f4c85d0a4b893090fe6605f255e272b270495e" + integrity sha512-K4sc8/OJ/y5uQPWJFACMExS2UIqF+t3vdQ2A9Mhl9tMsp70CXf0sp6Y9ENYju1K7XWwR5Clh8dkP2jO1Ntlg1g== + dependencies: + "@types/argparse" "1.0.33" + "@types/node" "8.5.8" + argparse "~1.0.9" + colors "~1.2.1" + +"@microsoft/tsdoc@0.12.14": + version "0.12.14" + resolved "https://registry.yarnpkg.com/@microsoft/tsdoc/-/tsdoc-0.12.14.tgz#0e0810a0a174e50e22dfe8edb30599840712f22d" + integrity sha512-518yewjSga1jLdiLrcmpMFlaba5P+50b0TWNFUpC+SL9Yzf0kMi57qw+bMl+rQ08cGqH1vLx4eg9YFUbZXgZ0Q== + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -2633,6 +2686,14 @@ "@types/node" "^10.12.18" rollup "^1.1.0" +"@pika/plugin-bundle-types@^0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@pika/plugin-bundle-types/-/plugin-bundle-types-0.6.0.tgz#589387aa3ec38b73045cdd373dfbaf845ed6b77d" + integrity sha512-T+2mUa1MP6MsjPQWN9IAMq9gH0JkLEjybRk8NYPDxbfVHmrS5+sBS+/e2wOA1wGn0XJMicuk/qdkIBAegepyOw== + dependencies: + "@microsoft/api-extractor" "^7.1.5" + rimraf "^2.6.3" + "@pika/plugin-standard-pkg@0.6.0": version "0.6.0" resolved "https://registry.yarnpkg.com/@pika/plugin-standard-pkg/-/plugin-standard-pkg-0.6.0.tgz#d0dcc4e08d48d2ed100d93bcd6bc829d4713c9c9" @@ -3208,6 +3269,11 @@ "@testing-library/dom" "^6.1.0" "@types/testing-library__react" "^9.1.0" +"@types/argparse@1.0.33": + version "1.0.33" + resolved "https://registry.yarnpkg.com/@types/argparse/-/argparse-1.0.33.tgz#2728669427cdd74a99e53c9f457ca2866a37c52d" + integrity sha512-VQgHxyPMTj3hIlq9SY1mctqx+Jj8kpQfoLvDlVSDNOyuYs8JYfkuY3OW/4+dO657yPmNhHpePRx0/Tje5ImNVQ== + "@types/babel__core@^7.1.0": version "7.1.2" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.2.tgz#608c74f55928033fce18b99b213c16be4b3d114f" @@ -3261,6 +3327,13 @@ resolved "https://registry.yarnpkg.com/@types/fbemitter/-/fbemitter-2.0.32.tgz#8ed204da0f54e9c8eaec31b1eec91e25132d082c" integrity sha1-jtIE2g9U6cjq7DGx7skeJRMtCCw= +"@types/fs-extra@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.0.4.tgz#b971134d162cc0497d221adde3dbb67502225599" + integrity sha512-DsknoBvD8s+RFfSGjmERJ7ZOP1HI0UZRA3FSI+Zakhrc/Gy26YQsLI+m5V5DHxroHRJqCDLKJp7Hixn8zyaF7g== + dependencies: + "@types/node" "*" + "@types/glob@^7.1.1": version "7.1.1" resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" @@ -3312,6 +3385,11 @@ dependencies: "@types/jest-diff" "*" +"@types/jju@~1.4.0": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@types/jju/-/jju-1.4.1.tgz#0a39f5f8e84fec46150a7b9ca985c3f89ad98e9f" + integrity sha512-LFt+YA7Lv2IZROMwokZKiPNORAV5N3huMs3IKnzlE430HWhWYZ8b+78HiwJXJJP1V2IEjinyJURuRJfGoaFSIA== + "@types/json-schema@^7.0.3": version "7.0.3" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636" @@ -3344,6 +3422,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.7.2.tgz#c4e63af5e8823ce9cc3f0b34f7b998c2171f0c44" integrity sha512-dyYO+f6ihZEtNPDcWNR1fkoTDf3zAK3lAABDze3mz6POyIercH0lEUawUFXlG8xaQZmm1yEBON/4TsYv/laDYg== +"@types/node@8.5.8": + version "8.5.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-8.5.8.tgz#92509422653f10e9c0ac18d87e0610b39f9821c7" + integrity sha512-8KmlRxwbKZfjUHFIt3q8TF5S2B+/E5BaAoo/3mgc5h6FJzqxXkCK/VMetO+IRDtwtU6HUvovHMBn+XRj7SV9Qg== + "@types/node@^10.12.18": version "10.14.16" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.16.tgz#4d690c96cbb7b2728afea0e260d680501b3da5cf" @@ -3446,6 +3529,11 @@ dependencies: "@types/yargs-parser" "*" +"@types/z-schema@3.16.31": + version "3.16.31" + resolved "https://registry.yarnpkg.com/@types/z-schema/-/z-schema-3.16.31.tgz#2eb1d00a5e4ec3fa58c76afde12e182b66dc5c1c" + integrity sha1-LrHQCl5Ow/pYx2r94S4YK2bcXBw= + "@typescript-eslint/eslint-plugin@1.13.0": version "1.13.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz#22fed9b16ddfeb402fd7bcde56307820f6ebc49f" @@ -4010,7 +4098,7 @@ are-we-there-yet@~1.1.2: delegates "^1.0.0" readable-stream "^2.0.6" -argparse@^1.0.7: +argparse@^1.0.7, argparse@~1.0.9: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== @@ -5753,6 +5841,11 @@ colors@^1.1.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== +colors@~1.2.1: + version "1.2.5" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.5.tgz#89c7ad9a374bc030df8013241f68136ed8835afc" + integrity sha512-erNRLao/Y3Fv54qUa0LBB+//Uf3YwMUmdJinN20yMXm9zdKKqH9wt7R9IIVZ+K7ShzfpLV/Zg8+VyrBJYB4lpg== + columnify@^1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" @@ -5790,7 +5883,7 @@ commander@2.9.0: dependencies: graceful-readlink ">= 1.0.0" -commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@^2.9.0, commander@~2.20.0: +commander@^2.11.0, commander@^2.19.0, commander@^2.20.0, commander@^2.7.1, commander@^2.9.0, commander@~2.20.0: version "2.20.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== @@ -8530,7 +8623,7 @@ from2@^2.1.0: inherits "^2.0.1" readable-stream "^2.0.0" -fs-extra@7.0.1, fs-extra@^7.0.0, fs-extra@^7.0.1: +fs-extra@7.0.1, fs-extra@^7.0.0, fs-extra@^7.0.1, fs-extra@~7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== @@ -10753,6 +10846,11 @@ jest@24.9.0: import-local "^2.0.0" jest-cli "^24.9.0" +jju@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" + integrity sha1-o6vicYryQaKykE+EpiWXDzia4yo= + js-beautify@^1.8.9: version "1.10.2" resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.10.2.tgz#88c9099cd6559402b124cfab18754936f8a7b178" @@ -11325,11 +11423,16 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168= -lodash.get@^4.4.2: +lodash.get@^4.0.0, lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= +lodash.isequal@^4.0.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= + lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -11400,7 +11503,7 @@ lodash.zip@^4.2.0: resolved "https://registry.yarnpkg.com/lodash.zip/-/lodash.zip-4.2.0.tgz#ec6662e4896408ed4ab6c542a3990b72cc080020" integrity sha1-7GZi5IlkCO1KtsVCo5kLcswIACA= -"lodash@>=3.5 <5", lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.0, lodash@^4.6.1: +"lodash@>=3.5 <5", lodash@^4.0.1, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.0, lodash@^4.6.1, lodash@~4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -13623,7 +13726,7 @@ path-key@^3.0.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.0.tgz#99a10d870a803bdd5ee6f0470e58dfcd2f9a54d3" integrity sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg== -path-parse@^1.0.6: +path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== @@ -16080,6 +16183,13 @@ resolve@1.12.0, resolve@^1.1.5, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.10.1 dependencies: path-parse "^1.0.6" +resolve@1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + integrity sha512-AicPrAC7Qu1JxPCZ9ZgCZlY35QgFnNqc+0LtbRNxnVw4TXvjQ72wnuL9JQcEBgXkI9JM8MsT9kaQoHcpCRJOYA== + dependencies: + path-parse "^1.0.5" + responselike@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" @@ -17854,7 +17964,7 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.5.1: +typescript@^3.5.1, typescript@~3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== @@ -18264,6 +18374,11 @@ validate-npm-package-name@^3.0.0: dependencies: builtins "^1.0.3" +validator@^8.0.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-8.2.0.tgz#3c1237290e37092355344fef78c231249dab77b9" + integrity sha512-Yw5wW34fSv5spzTXNkokD6S6/Oq92d8q/t14TqsS3fAiA1RYnxSFSIZ+CY3n6PGGRCq5HhJTSepQvFUS2QUDxA== + value-equal@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.4.0.tgz#c5bdd2f54ee093c04839d71ce2e4758a6890abc7" @@ -19149,3 +19264,14 @@ yargs@^9.0.0: which-module "^2.0.0" y18n "^3.2.1" yargs-parser "^7.0.0" + +z-schema@~3.18.3: + version "3.18.4" + resolved "https://registry.yarnpkg.com/z-schema/-/z-schema-3.18.4.tgz#ea8132b279533ee60be2485a02f7e3e42541a9a2" + integrity sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw== + dependencies: + lodash.get "^4.0.0" + lodash.isequal "^4.0.0" + validator "^8.0.0" + optionalDependencies: + commander "^2.7.1" From 70393af94ddd030de56bfcadcd17f00e6762dfa9 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Mon, 16 Sep 2019 08:20:39 +0200 Subject: [PATCH 07/47] hotfix for type generation --- packages/react-async/src/helpers.tsx | 20 ++++++++++++++------ packages/react-async/src/reducer.ts | 9 +++++++++ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/packages/react-async/src/helpers.tsx b/packages/react-async/src/helpers.tsx index 1c1acdc8..0e2f0784 100644 --- a/packages/react-async/src/helpers.tsx +++ b/packages/react-async/src/helpers.tsx @@ -8,8 +8,16 @@ import { RejectedChildren, SettledChildren, AsyncState, + AbstractState, } from "./Async" +/** + * Due to https://github.com/microsoft/web-build-tools/issues/1050, we need + * AbstractState imported in this file, even though it is only used implicitly. + * This _uses_ AbstractState so it is not accidentally removed by someone. + */ +declare type ImportWorkaround = AbstractState; + // these were exported as type, but never existed // export declare function IfLoading(props: { // children?: PendingChildren @@ -80,8 +88,8 @@ export const IfFulfilled = ({ persist?: boolean state: AsyncState }) => ( - <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} -) + <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} + ) /** * Renders only when promise is rejected. @@ -99,10 +107,10 @@ export const IfRejected = ({ persist?: boolean state: AsyncState }) => ( - <> - {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} - -) + <> + {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} + + ) /** * Renders only when promise is fulfilled or rejected. diff --git a/packages/react-async/src/reducer.ts b/packages/react-async/src/reducer.ts index 13b63fa2..4b46af64 100644 --- a/packages/react-async/src/reducer.ts +++ b/packages/react-async/src/reducer.ts @@ -7,8 +7,17 @@ import { AsyncFulfilled, AsyncRejected, AsyncInitial, + AbstractState, } from "./Async" +/** + * Due to https://github.com/microsoft/web-build-tools/issues/1050, we need + * AbstractState imported in this file, even though it is only used implicitly. + * This _uses_ AbstractState so it is not accidentally removed by someone. + */ +declare type ImportWorkaround = AbstractState; + + export enum actionTypes { start = "start", cancel = "cancel", From 5d545c951cdf90c00cc6f9b7e029402adf07f736 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Mon, 16 Sep 2019 17:24:05 +0200 Subject: [PATCH 08/47] add missing exports, lint --- package.json | 4 +-- packages/react-async/src/helpers.tsx | 14 +++++----- packages/react-async/src/index.ts | 42 ++++++++++++++++++++++++++-- packages/react-async/src/reducer.ts | 3 +- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/package.json b/package.json index 296428aa..200b6ff7 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "start": "run-p start:*", "start:examples": "now dev", "start:storybook": "start-storybook -p 6006", - "lint": "eslint packages/*/src/*.{j,t}{s,sx}", + "lint": "eslint packages/*/src/*.{js,ts,tsx}", "test": "jest packages/*/src/*.spec.js", "test:watch": "yarn test -- --watch", "test:devtools": "jest react-async-devtools/src", @@ -76,4 +76,4 @@ "react-dom": "16.9.0", "storybook-chromatic": "2.2.2" } -} +} \ No newline at end of file diff --git a/packages/react-async/src/helpers.tsx b/packages/react-async/src/helpers.tsx index 0e2f0784..e70d1c8b 100644 --- a/packages/react-async/src/helpers.tsx +++ b/packages/react-async/src/helpers.tsx @@ -16,7 +16,7 @@ import { * AbstractState imported in this file, even though it is only used implicitly. * This _uses_ AbstractState so it is not accidentally removed by someone. */ -declare type ImportWorkaround = AbstractState; +declare type ImportWorkaround = AbstractState // these were exported as type, but never existed // export declare function IfLoading(props: { @@ -88,8 +88,8 @@ export const IfFulfilled = ({ persist?: boolean state: AsyncState }) => ( - <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} - ) + <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} +) /** * Renders only when promise is rejected. @@ -107,10 +107,10 @@ export const IfRejected = ({ persist?: boolean state: AsyncState }) => ( - <> - {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} - - ) + <> + {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} + +) /** * Renders only when promise is fulfilled or rejected. diff --git a/packages/react-async/src/index.ts b/packages/react-async/src/index.ts index 5c8aba24..7b3ad043 100644 --- a/packages/react-async/src/index.ts +++ b/packages/react-async/src/index.ts @@ -1,8 +1,46 @@ import Async from "./Async" -export { default as Async, createInstance } from "./Async" -export { default as useAsync, useFetch } from "./useAsync" +export { + default as Async, + createInstance, + PromiseFn, + InitialChildren, + PendingChildren, + FulfilledChildren, + Start, + Cancel, + Reject, + AsyncProps, + RejectedChildren, + SettledChildren, + DeferFn, + Fulfill, + AsyncAction, + AbstractState, + AsyncInitial, + AsyncPending, + AsyncFulfilled, + AsyncChildren, + AsyncOptions, + AsyncRejected, + AsyncState, +} from "./Async" +export { default as useAsync, useFetch, FetchOptions } from "./useAsync" export default Async export { statusTypes } from "./status" export { default as globalScope } from "./globalScope" export * from "./helpers" export * from "./reducer" + +/* +> RejectedChildren +> SettledChildren +> PromiseFn +> DeferFn +> Fulfill +> AsyncAction +> AbstractState +> AsyncInitial +> AsyncPending +> AsyncFulfilled +> AsyncInitialWithout +*/ diff --git a/packages/react-async/src/reducer.ts b/packages/react-async/src/reducer.ts index 4b46af64..bb8f4c77 100644 --- a/packages/react-async/src/reducer.ts +++ b/packages/react-async/src/reducer.ts @@ -15,8 +15,7 @@ import { * AbstractState imported in this file, even though it is only used implicitly. * This _uses_ AbstractState so it is not accidentally removed by someone. */ -declare type ImportWorkaround = AbstractState; - +declare type ImportWorkaround = AbstractState export enum actionTypes { start = "start", From 6aaa0457700c9df0a6dd36d0938b580952cfa240 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 07:25:03 +0200 Subject: [PATCH 09/47] prevent multiple different typescript versions to be installed --- package.json | 3 +++ yarn.lock | 18 ++++-------------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 200b6ff7..25d0d64f 100644 --- a/package.json +++ b/package.json @@ -75,5 +75,8 @@ "react-async": "^8.0.0-alpha.0", "react-dom": "16.9.0", "storybook-chromatic": "2.2.2" + }, + "resolutions": { + "typescript": "3.6.3" } } \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 16600a62..a5d774de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17964,20 +17964,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.5.1, typescript@~3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977" - integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g== - -typescript@^3.6.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54" - integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw== - -typescript@~3.4.0: - version "3.4.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" - integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== +typescript@3.6.3, typescript@^3.5.1, typescript@^3.6.2, typescript@~3.4.0, typescript@~3.5.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" + integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== ua-parser-js@^0.7.18, ua-parser-js@^0.7.19: version "0.7.20" From 65eebf3bb6f00db70f8d97e337e819b42dd5e876 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 07:27:18 +0200 Subject: [PATCH 10/47] move common types to types.ts --- packages/react-async/src/Async.tsx | 152 ++------------------------ packages/react-async/src/helpers.tsx | 2 +- packages/react-async/src/index.ts | 19 +--- packages/react-async/src/reducer.ts | 2 +- packages/react-async/src/status.ts | 2 +- packages/react-async/src/types.ts | 136 +++++++++++++++++++++++ packages/react-async/src/useAsync.tsx | 2 +- 7 files changed, 153 insertions(+), 162 deletions(-) create mode 100644 packages/react-async/src/types.ts diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index 7291c64a..5d3e9d06 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -5,147 +5,17 @@ import { IfInitial, IfPending, IfFulfilled, IfRejected, IfSettled } from "./help // @ts-ignore import propTypes from "./propTypes" import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" - -export declare type AsyncChildren = ((state: AsyncState) => React.ReactNode) | React.ReactNode -export declare type InitialChildren = - | ((state: AsyncInitial) => React.ReactNode) - | React.ReactNode -export declare type PendingChildren = - | ((state: AsyncPending) => React.ReactNode) - | React.ReactNode -export declare type FulfilledChildren = - | ((data: T, state: AsyncFulfilled) => React.ReactNode) - | React.ReactNode -export declare type RejectedChildren = - | ((error: Error, state: AsyncRejected) => React.ReactNode) - | React.ReactNode -export declare type SettledChildren = - | ((state: AsyncFulfilled | AsyncRejected) => React.ReactNode) - | React.ReactNode - -export declare type PromiseFn = (props: AsyncProps, controller: AbortController) => Promise -export declare type DeferFn = ( - args: any[], - props: AsyncProps, - controller: AbortController -) => Promise - -export interface AbstractAction { - type: string - meta: { counter: number; [meta: string]: any } -} -export type Meta = AbstractAction["meta"] - -export declare type Start = AbstractAction & { type: "start"; payload: () => Promise } -export declare type Cancel = AbstractAction & { type: "cancel" } -export declare type Fulfill = AbstractAction & { type: "fulfill"; payload: T } -export declare type Reject = AbstractAction & { type: "reject"; payload: Error; error: true } -export declare type AsyncAction = Start | Cancel | Fulfill | Reject - -export declare interface AsyncOptions { - promise?: Promise - promiseFn?: PromiseFn - deferFn?: DeferFn - watch?: any - watchFn?: (props: AsyncProps, prevProps: AsyncProps) => any - initialValue?: T - onResolve?: (data: T) => void - onReject?: (error: Error) => void - reducer?: ( - state: AsyncState, - action: AsyncAction, - internalReducer: (state: AsyncState, action: AsyncAction) => AsyncState - ) => AsyncState - dispatcher?: ( - action: AsyncAction, - internalDispatch: (action: AsyncAction) => void, - props: AsyncProps - ) => void - debugLabel?: string - [prop: string]: any -} - -export declare interface AsyncProps extends AsyncOptions { - children?: AsyncChildren -} - -export interface AbstractState { - initialValue?: T | Error - counter: number - promise: Promise - run: (...args: any[]) => void - reload: () => void - cancel: () => void - setData: (data: T, callback?: () => void) => T - setError: (error: Error, callback?: () => void) => Error -} - -export declare type AsyncInitial> = S & { - initialValue?: undefined - data: undefined - error: undefined - value: undefined - startedAt: undefined - finishedAt: undefined - status: "initial" - isInitial: false - isPending: false - isLoading: false - isFulfilled: false - isResolved: false - isRejected: false - isSettled: false -} -export declare type AsyncPending> = S & { - data: T | undefined - error: Error | undefined - value: T | Error | undefined - startedAt: Date - finishedAt: undefined - status: "pending" - isInitial: false - isPending: true - isLoading: true - isFulfilled: false - isResolved: false - isRejected: false - isSettled: false -} -export declare type AsyncFulfilled> = S & { - data: T - error: undefined - value: T - startedAt: Date - finishedAt: Date - status: "fulfilled" - isInitial: false - isPending: false - isLoading: false - isFulfilled: true - isResolved: true - isRejected: false - isSettled: true -} -export declare type AsyncRejected> = S & { - data: T | undefined - error: Error - value: Error - startedAt: Date - finishedAt: Date - status: "rejected" - isInitial: false - isPending: false - isLoading: false - isFulfilled: false - isResolved: false - isRejected: true - isSettled: true -} -export declare type AsyncState = AbstractState> = - | AsyncInitial - | AsyncPending - | AsyncFulfilled - | AsyncRejected +import { + AsyncProps, + AsyncState, + InitialChildren, + PendingChildren, + FulfilledChildren, + SettledChildren, + RejectedChildren, + AbstractState, + AsyncAction, +} from "./types" export declare class Async extends React.Component, AsyncState> {} diff --git a/packages/react-async/src/helpers.tsx b/packages/react-async/src/helpers.tsx index e70d1c8b..a27ec44f 100644 --- a/packages/react-async/src/helpers.tsx +++ b/packages/react-async/src/helpers.tsx @@ -9,7 +9,7 @@ import { SettledChildren, AsyncState, AbstractState, -} from "./Async" +} from "./types" /** * Due to https://github.com/microsoft/web-build-tools/issues/1050, we need diff --git a/packages/react-async/src/index.ts b/packages/react-async/src/index.ts index 7b3ad043..1a427278 100644 --- a/packages/react-async/src/index.ts +++ b/packages/react-async/src/index.ts @@ -1,7 +1,6 @@ import Async from "./Async" +export { default as Async, createInstance } from "./Async" export { - default as Async, - createInstance, PromiseFn, InitialChildren, PendingChildren, @@ -23,24 +22,10 @@ export { AsyncOptions, AsyncRejected, AsyncState, -} from "./Async" +} from "./types" export { default as useAsync, useFetch, FetchOptions } from "./useAsync" export default Async export { statusTypes } from "./status" export { default as globalScope } from "./globalScope" export * from "./helpers" export * from "./reducer" - -/* -> RejectedChildren -> SettledChildren -> PromiseFn -> DeferFn -> Fulfill -> AsyncAction -> AbstractState -> AsyncInitial -> AsyncPending -> AsyncFulfilled -> AsyncInitialWithout -*/ diff --git a/packages/react-async/src/reducer.ts b/packages/react-async/src/reducer.ts index bb8f4c77..a72f186d 100644 --- a/packages/react-async/src/reducer.ts +++ b/packages/react-async/src/reducer.ts @@ -8,7 +8,7 @@ import { AsyncRejected, AsyncInitial, AbstractState, -} from "./Async" +} from "./types" /** * Due to https://github.com/microsoft/web-build-tools/issues/1050, we need diff --git a/packages/react-async/src/status.ts b/packages/react-async/src/status.ts index 34b6df7f..2af3383b 100644 --- a/packages/react-async/src/status.ts +++ b/packages/react-async/src/status.ts @@ -1,4 +1,4 @@ -import { PromiseFn } from "./Async" +import { PromiseFn } from "./types" export enum statusTypes { initial = "initial", diff --git a/packages/react-async/src/types.ts b/packages/react-async/src/types.ts new file mode 100644 index 00000000..4cb38443 --- /dev/null +++ b/packages/react-async/src/types.ts @@ -0,0 +1,136 @@ +export type AsyncChildren = ((state: AsyncState) => React.ReactNode) | React.ReactNode +export type InitialChildren = ((state: AsyncInitial) => React.ReactNode) | React.ReactNode +export type PendingChildren = ((state: AsyncPending) => React.ReactNode) | React.ReactNode +export type FulfilledChildren = + | ((data: T, state: AsyncFulfilled) => React.ReactNode) + | React.ReactNode +export type RejectedChildren = + | ((error: Error, state: AsyncRejected) => React.ReactNode) + | React.ReactNode +export type SettledChildren = + | ((state: AsyncFulfilled | AsyncRejected) => React.ReactNode) + | React.ReactNode + +export type PromiseFn = (props: AsyncProps, controller: AbortController) => Promise +export type DeferFn = ( + args: any[], + props: AsyncProps, + controller: AbortController +) => Promise + +export interface AbstractAction { + type: string + meta: { counter: number; [meta: string]: any } +} +export type Meta = AbstractAction["meta"] + +export type Start = AbstractAction & { type: "start"; payload: () => Promise } +export type Cancel = AbstractAction & { type: "cancel" } +export type Fulfill = AbstractAction & { type: "fulfill"; payload: T } +export type Reject = AbstractAction & { type: "reject"; payload: Error; error: true } +export type AsyncAction = Start | Cancel | Fulfill | Reject + +export interface AsyncOptions { + promise?: Promise + promiseFn?: PromiseFn + deferFn?: DeferFn + watch?: any + watchFn?: (props: AsyncProps, prevProps: AsyncProps) => any + initialValue?: T + onResolve?: (data: T) => void + onReject?: (error: Error) => void + reducer?: ( + state: AsyncState, + action: AsyncAction, + internalReducer: (state: AsyncState, action: AsyncAction) => AsyncState + ) => AsyncState + dispatcher?: ( + action: AsyncAction, + internalDispatch: (action: AsyncAction) => void, + props: AsyncProps + ) => void + debugLabel?: string + [prop: string]: any +} + +export interface AsyncProps extends AsyncOptions { + children?: AsyncChildren +} + +export interface AbstractState { + initialValue?: T | Error + counter: number + promise: Promise + run: (...args: any[]) => void + reload: () => void + cancel: () => void + setData: (data: T, callback?: () => void) => T + setError: (error: Error, callback?: () => void) => Error +} + +export type AsyncInitial> = S & { + initialValue?: undefined + data: undefined + error: undefined + value: undefined + startedAt: undefined + finishedAt: undefined + status: "initial" + isInitial: false + isPending: false + isLoading: false + isFulfilled: false + isResolved: false + isRejected: false + isSettled: false +} +export type AsyncPending> = S & { + data: T | undefined + error: Error | undefined + value: T | Error | undefined + startedAt: Date + finishedAt: undefined + status: "pending" + isInitial: false + isPending: true + isLoading: true + isFulfilled: false + isResolved: false + isRejected: false + isSettled: false +} +export type AsyncFulfilled> = S & { + data: T + error: undefined + value: T + startedAt: Date + finishedAt: Date + status: "fulfilled" + isInitial: false + isPending: false + isLoading: false + isFulfilled: true + isResolved: true + isRejected: false + isSettled: true +} +export type AsyncRejected> = S & { + data: T | undefined + error: Error + value: Error + startedAt: Date + finishedAt: Date + status: "rejected" + isInitial: false + isPending: false + isLoading: false + isFulfilled: false + isResolved: false + isRejected: true + isSettled: true +} +export type AsyncState = AbstractState> = + | AsyncInitial + | AsyncPending + | AsyncFulfilled + | AsyncRejected diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index 8273da2c..dda0c8c5 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -3,7 +3,7 @@ import { useCallback, useDebugValue, useEffect, useMemo, useRef, useReducer } fr import globalScope from "./globalScope" import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" -import { AsyncOptions, AsyncState, AbstractState, PromiseFn, Meta } from "./Async" +import { AsyncOptions, AsyncState, AbstractState, PromiseFn, Meta } from "./types" export interface FetchOptions extends AsyncOptions { defer?: boolean From d370e89d5e3ffda4c7a4c3cfbd42b50c6397fa5c Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 07:48:24 +0200 Subject: [PATCH 11/47] jest config: also transform tsx files --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index 6205c683..b5124c80 100644 --- a/jest.config.js +++ b/jest.config.js @@ -18,7 +18,7 @@ module.exports = { verbose: true, bail: true, transform: { - "^.+\\.[tj]s$": "babel-jest", + "^.+\\.[tj]sx?$": "babel-jest", }, projects: ["/packages/*"], setupFiles: ["/jest.setup.js"], From 430f7c3b9c203a2ae74f378ef4bbb4c4a9849cf2 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 07:48:41 +0200 Subject: [PATCH 12/47] readability of globalScope --- packages/react-async/src/globalScope.ts | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/packages/react-async/src/globalScope.ts b/packages/react-async/src/globalScope.ts index e1fa63ac..5804b37d 100644 --- a/packages/react-async/src/globalScope.ts +++ b/packages/react-async/src/globalScope.ts @@ -1,19 +1,21 @@ /* istanbul ignore file */ +declare type GlobalScope = { + __REACT_ASYNC__: any + AbortController?: typeof AbortController + fetch: typeof fetch +} + /** * Universal global scope object. In the browser this is `self`, in Node.js and React Native it's `global`. * This file is excluded from coverage reporting because these globals are environment-specific so we can't test them all. */ -const globalScope = ((): { - __REACT_ASYNC__?: any - AbortController?: typeof AbortController - fetch: Window["fetch"] -} => { - if (typeof self === "object" && self.self === self) return self as any - if (typeof global === "object" && global.global === global) return global as any - if (typeof global === "object" && global.GLOBAL === global) return global as any - return {} as any // fallback that relies on imported modules to be singletons -})() +const globalScope = (() => { + if (typeof self === "object" && self.self === self) return self + if (typeof global === "object" && global.global === global) return global + if (typeof global === "object" && global.GLOBAL === global) return global + return {} // fallback that relies on imported modules to be singletons +})() as GlobalScope /** * Globally available object used to connect the DevTools to all React Async instances. From f3d228439080631af100f39cec6985bd03a49881 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 08:28:08 +0200 Subject: [PATCH 13/47] move inline typecast to variable --- packages/react-async/src/Async.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index 5d3e9d06..3269b0a4 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -222,11 +222,8 @@ export const createInstance = ( render() { const { children } = this.props if (typeof children === "function") { - return ( - - {(children as ((state: AsyncState) => React.ReactNode))(this.state)} - - ) + const render = children as (state: AsyncState) => React.ReactNode + return {render(this.state)} } if (children !== undefined && children !== null) { return {children} From 2e5333c8d8fb5f15a2813a43fee1cbdac2693836 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 09:32:01 +0200 Subject: [PATCH 14/47] better typings for createInstance and default Async --- packages/react-async/src/Async.tsx | 75 +++++++++++++++++++++--------- 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index 3269b0a4..aa8b49df 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -17,17 +17,46 @@ import { AsyncAction, } from "./types" -export declare class Async extends React.Component, AsyncState> {} +interface InitialProps { + children?: InitialChildren + persist?: boolean +} +interface PendingProps { + children?: PendingChildren + initial?: boolean +} +interface FulfilledProps { + children?: FulfilledChildren + persist?: boolean +} +interface RejectedProps { + children?: RejectedChildren + persist?: boolean +} +interface SettledProps { + children?: SettledChildren + persist?: boolean +} + +class Async extends React.Component, AsyncState> {} +type GenericAsync = typeof Async & { + Initial(props: InitialProps): JSX.Element + Pending(props: PendingProps): JSX.Element + Loading(props: PendingProps): JSX.Element + Fulfilled(props: FulfilledProps): JSX.Element + Resolved(props: FulfilledProps): JSX.Element + Rejected(props: RejectedProps): JSX.Element + Settled(props: SettledProps): JSX.Element +} -interface AsyncConstructor extends ComponentClass> { - new (): Async - Initial: React.FC<{ children?: InitialChildren; persist?: boolean }> - Pending: React.FC<{ children?: PendingChildren; initial?: boolean }> - Loading: React.FC<{ children?: PendingChildren; initial?: boolean }> - Fulfilled: React.FC<{ children?: FulfilledChildren; persist?: boolean }> - Resolved: React.FC<{ children?: FulfilledChildren; persist?: boolean }> - Rejected: React.FC<{ children?: RejectedChildren; persist?: boolean }> - Settled: React.FC<{ children?: SettledChildren; persist?: boolean }> +type AsyncConstructor = React.ComponentClass> & { + Initial: React.FC> + Pending: React.FC> + Loading: React.FC> + Fulfilled: React.FC> + Resolved: React.FC> + Rejected: React.FC> + Settled: React.FC> } /** @@ -42,7 +71,7 @@ export const createInstance = ( type Props = AsyncProps - const Async: AsyncConstructor = class Async extends React.Component> { + class Async extends React.Component> { private mounted = false private counter = 0 private args: any[] = [] @@ -230,7 +259,7 @@ export const createInstance = ( } return null } - } as any + } if (propTypes) (Async as React.ComponentClass).propTypes = propTypes.Async @@ -266,16 +295,16 @@ export const createInstance = ( AsyncRejected.displayName = `${displayName}.Rejected` AsyncSettled.displayName = `${displayName}.Settled` - Async.displayName = displayName - Async.Initial = AsyncInitial - Async.Pending = AsyncPending - Async.Loading = AsyncPending // alias - Async.Fulfilled = AsyncFulfilled - Async.Resolved = AsyncFulfilled // alias - Async.Rejected = AsyncRejected - Async.Settled = AsyncSettled - - return Async + return Object.assign(Async, { + displayName: displayName, + Initial: AsyncInitial, + Pending: AsyncPending, + Loading: AsyncPending, // alias + Fulfilled: AsyncFulfilled, + Resolved: AsyncFulfilled, // alias + Rejected: AsyncRejected, + Settled: AsyncSettled, + }) } -export default createInstance() +export default createInstance() as GenericAsync From 436e49b85a9f1e4a65d2ad6ecf885bf175608acb Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 09:35:59 +0200 Subject: [PATCH 15/47] remove unneccesary genererics --- packages/react-async/src/Async.tsx | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index aa8b49df..e38e341a 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -264,29 +264,19 @@ export const createInstance = ( if (propTypes) (Async as React.ComponentClass).propTypes = propTypes.Async const AsyncInitial: AsyncConstructor["Initial"] = props => ( - - {(st: AsyncState>) => } - + {(st: AsyncState) => } ) const AsyncPending: AsyncConstructor["Pending"] = props => ( - - {(st: AsyncState>) => } - + {(st: AsyncState) => } ) const AsyncFulfilled: AsyncConstructor["Fulfilled"] = props => ( - - {(st: AsyncState>) => } - + {(st: AsyncState) => } ) const AsyncRejected: AsyncConstructor["Rejected"] = props => ( - - {(st: AsyncState>) => } - + {(st: AsyncState) => } ) const AsyncSettled: AsyncConstructor["Settled"] = props => ( - - {(st: AsyncState>) => } - + {(st: AsyncState) => } ) AsyncInitial.displayName = `${displayName}.Initial` From 8dd1086858989823f89823e8ab190b5149717589 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 09:37:06 +0200 Subject: [PATCH 16/47] remove accidental types --- packages/react-async/src/helpers.tsx | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/packages/react-async/src/helpers.tsx b/packages/react-async/src/helpers.tsx index a27ec44f..825abf63 100644 --- a/packages/react-async/src/helpers.tsx +++ b/packages/react-async/src/helpers.tsx @@ -18,18 +18,6 @@ import { */ declare type ImportWorkaround = AbstractState -// these were exported as type, but never existed -// export declare function IfLoading(props: { -// children?: PendingChildren -// initial?: boolean -// state: AsyncState -// }): React.ReactNode -// export declare function IfResolved(props: { -// children?: FulfilledChildren -// persist?: boolean -// state: AsyncState -// }): React.ReactNode - const nullify = (children: T | undefined): T | null => children === undefined ? null : children @@ -88,8 +76,8 @@ export const IfFulfilled = ({ persist?: boolean state: AsyncState }) => ( - <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} -) + <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} + ) /** * Renders only when promise is rejected. @@ -107,10 +95,10 @@ export const IfRejected = ({ persist?: boolean state: AsyncState }) => ( - <> - {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} - -) + <> + {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} + + ) /** * Renders only when promise is fulfilled or rejected. From e08b6ed0954b3a8aca2e5ab7cd4ded5ef728c422 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 09:42:01 +0200 Subject: [PATCH 17/47] inline nullify into render --- packages/react-async/src/helpers.tsx | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/packages/react-async/src/helpers.tsx b/packages/react-async/src/helpers.tsx index 825abf63..a7ee0ca7 100644 --- a/packages/react-async/src/helpers.tsx +++ b/packages/react-async/src/helpers.tsx @@ -18,13 +18,14 @@ import { */ declare type ImportWorkaround = AbstractState -const nullify = (children: T | undefined): T | null => - children === undefined ? null : children - -const renderFn = ( - children: React.ReactNode | ((...args: any[]) => React.ReactNode), - ...args: any[] -) => nullify(typeof children === "function" ? (children as any)(...args) : children) +type ChildrenFn = (...args: any[]) => React.ReactNode +const renderFn = (children: React.ReactNode | ChildrenFn, ...args: any[]) => { + if (typeof children === "function") { + const render = children as ChildrenFn + return render(...args) + } + return children === undefined ? null : children +} /** * Renders only when no promise has started or completed yet. @@ -76,8 +77,8 @@ export const IfFulfilled = ({ persist?: boolean state: AsyncState }) => ( - <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} - ) + <>{state.isFulfilled || (persist && state.data) ? renderFn(children, state.data, state) : null} +) /** * Renders only when promise is rejected. @@ -95,10 +96,10 @@ export const IfRejected = ({ persist?: boolean state: AsyncState }) => ( - <> - {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} - - ) + <> + {state.isRejected || (persist && state.error) ? renderFn(children, state.error, state) : null} + +) /** * Renders only when promise is fulfilled or rejected. From d70941637add2408a3cb773cb69032e46a1b1fa2 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 09:44:37 +0200 Subject: [PATCH 18/47] wildcard-export all types --- packages/react-async/src/index.ts | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/packages/react-async/src/index.ts b/packages/react-async/src/index.ts index 1a427278..07f5e958 100644 --- a/packages/react-async/src/index.ts +++ b/packages/react-async/src/index.ts @@ -1,28 +1,6 @@ import Async from "./Async" export { default as Async, createInstance } from "./Async" -export { - PromiseFn, - InitialChildren, - PendingChildren, - FulfilledChildren, - Start, - Cancel, - Reject, - AsyncProps, - RejectedChildren, - SettledChildren, - DeferFn, - Fulfill, - AsyncAction, - AbstractState, - AsyncInitial, - AsyncPending, - AsyncFulfilled, - AsyncChildren, - AsyncOptions, - AsyncRejected, - AsyncState, -} from "./types" +export * from "./types" export { default as useAsync, useFetch, FetchOptions } from "./useAsync" export default Async export { statusTypes } from "./status" From 9c5d5ce0ea9178514fd1f18c1d0d176e0eb62e28 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 20 Sep 2019 10:17:10 +0200 Subject: [PATCH 19/47] introduce ReducerAsyncState type, type promise as optional undefined --- packages/react-async/src/Async.tsx | 7 +++-- packages/react-async/src/helpers.tsx | 11 +++++++- packages/react-async/src/reducer.ts | 39 +++++++++++++++------------ packages/react-async/src/types.ts | 17 +++++++++--- packages/react-async/src/useAsync.tsx | 24 ++++++++++++++++- 5 files changed, 73 insertions(+), 25 deletions(-) diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index e38e341a..9f072ee6 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -13,8 +13,8 @@ import { FulfilledChildren, SettledChildren, RejectedChildren, - AbstractState, AsyncAction, + ReducerAsyncState, } from "./types" interface InitialProps { @@ -112,7 +112,10 @@ export const createInstance = ( const { devToolsDispatcher } = globalScope.__REACT_ASYNC__ const _reducer = props.reducer || defaultProps.reducer const _dispatcher = props.dispatcher || defaultProps.dispatcher || devToolsDispatcher - const reducer: (state: AsyncState, action: AsyncAction) => AsyncState = _reducer + const reducer: ( + state: ReducerAsyncState, + action: AsyncAction + ) => ReducerAsyncState = _reducer ? (state, action) => _reducer(state, action, asyncReducer) : asyncReducer const dispatch = dispatchMiddleware((action, callback) => { diff --git a/packages/react-async/src/helpers.tsx b/packages/react-async/src/helpers.tsx index a7ee0ca7..af370457 100644 --- a/packages/react-async/src/helpers.tsx +++ b/packages/react-async/src/helpers.tsx @@ -9,6 +9,10 @@ import { SettledChildren, AsyncState, AbstractState, + AsyncInitial, + AsyncFulfilled, + AsyncPending, + AsyncRejected, } from "./types" /** @@ -16,7 +20,12 @@ import { * AbstractState imported in this file, even though it is only used implicitly. * This _uses_ AbstractState so it is not accidentally removed by someone. */ -declare type ImportWorkaround = AbstractState +declare type ImportWorkaround = + | AbstractState + | AsyncInitial + | AsyncFulfilled + | AsyncPending + | AsyncRejected type ChildrenFn = (...args: any[]) => React.ReactNode const renderFn = (children: React.ReactNode | ChildrenFn, ...args: any[]) => { diff --git a/packages/react-async/src/reducer.ts b/packages/react-async/src/reducer.ts index a72f186d..742ca6c5 100644 --- a/packages/react-async/src/reducer.ts +++ b/packages/react-async/src/reducer.ts @@ -8,6 +8,8 @@ import { AsyncRejected, AsyncInitial, AbstractState, + ReducerAsyncState, + ReducerBaseState, } from "./types" /** @@ -32,20 +34,20 @@ export const init = ({ initialValue?: Error | T promise?: Promise promiseFn?: PromiseFn -}): AsyncState => ({ - initialValue, - data: initialValue instanceof Error ? undefined : initialValue, - error: initialValue instanceof Error ? initialValue : undefined, - value: initialValue, - startedAt: promise || promiseFn ? new Date() : undefined, - finishedAt: initialValue ? new Date() : undefined, - ...getStatusProps(getInitialStatus(initialValue, promise || promiseFn)), - counter: 0, - // @ts-ignore see #92 - promise: undefined, -}) +}) => + ({ + initialValue, + data: initialValue instanceof Error ? undefined : initialValue, + error: initialValue instanceof Error ? initialValue : undefined, + value: initialValue, + startedAt: promise || promiseFn ? new Date() : undefined, + finishedAt: initialValue ? new Date() : undefined, + ...getStatusProps(getInitialStatus(initialValue, promise || promiseFn)), + counter: 0, + promise: undefined, + } as ReducerAsyncState) -export const reducer = (state: AsyncState, action: AsyncAction) => { +export const reducer = (state: ReducerAsyncState, action: AsyncAction) => { switch (action.type) { case actionTypes.start: return { @@ -55,7 +57,7 @@ export const reducer = (state: AsyncState, action: AsyncAction) => { ...getStatusProps(statusTypes.pending), counter: action.meta.counter, promise: action.meta.promise, - } as AsyncPending + } as AsyncPending> case actionTypes.cancel: return { ...state, @@ -64,7 +66,10 @@ export const reducer = (state: AsyncState, action: AsyncAction) => { ...getStatusProps(getIdleStatus(state.error || state.data)), counter: action.meta.counter, promise: action.meta.promise, - } as AsyncInitial | AsyncFulfilled | AsyncRejected + } as + | AsyncInitial> + | AsyncFulfilled> + | AsyncRejected> case actionTypes.fulfill: return { ...state, @@ -74,7 +79,7 @@ export const reducer = (state: AsyncState, action: AsyncAction) => { finishedAt: new Date(), ...getStatusProps(statusTypes.fulfilled), promise: action.meta.promise, - } as AsyncFulfilled + } as AsyncFulfilled> case actionTypes.reject: return { ...state, @@ -83,7 +88,7 @@ export const reducer = (state: AsyncState, action: AsyncAction) => { finishedAt: new Date(), ...getStatusProps(statusTypes.rejected), promise: action.meta.promise, - } as AsyncRejected + } as AsyncRejected> default: return state } diff --git a/packages/react-async/src/types.ts b/packages/react-async/src/types.ts index 4cb38443..e94afbfa 100644 --- a/packages/react-async/src/types.ts +++ b/packages/react-async/src/types.ts @@ -40,9 +40,9 @@ export interface AsyncOptions { onResolve?: (data: T) => void onReject?: (error: Error) => void reducer?: ( - state: AsyncState, + state: ReducerAsyncState, action: AsyncAction, - internalReducer: (state: AsyncState, action: AsyncAction) => AsyncState + internalReducer: (state: ReducerAsyncState, action: AsyncAction) => ReducerAsyncState ) => AsyncState dispatcher?: ( action: AsyncAction, @@ -60,7 +60,7 @@ export interface AsyncProps extends AsyncOptions { export interface AbstractState { initialValue?: T | Error counter: number - promise: Promise + promise: Promise | undefined run: (...args: any[]) => void reload: () => void cancel: () => void @@ -129,8 +129,17 @@ export type AsyncRejected> = S & { isRejected: true isSettled: true } -export type AsyncState = AbstractState> = + +type BaseAsyncState = | AsyncInitial | AsyncPending | AsyncFulfilled | AsyncRejected + +export type ReducerBaseState = Omit< + AbstractState, + "run" | "reload" | "cancel" | "setData" | "setError" +> +export type ReducerAsyncState = BaseAsyncState> + +export type AsyncState = AbstractState> = BaseAsyncState diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index dda0c8c5..58542a82 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -3,7 +3,29 @@ import { useCallback, useDebugValue, useEffect, useMemo, useRef, useReducer } fr import globalScope from "./globalScope" import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" -import { AsyncOptions, AsyncState, AbstractState, PromiseFn, Meta } from "./types" +import { + AsyncOptions, + AsyncState, + AbstractState, + PromiseFn, + Meta, + AsyncInitial, + AsyncFulfilled, + AsyncPending, + AsyncRejected, +} from "./types" + +/** + * Due to https://github.com/microsoft/web-build-tools/issues/1050, we need + * AbstractState imported in this file, even though it is only used implicitly. + * This _uses_ AbstractState so it is not accidentally removed by someone. + */ +declare type ImportWorkaround = + | AbstractState + | AsyncInitial + | AsyncFulfilled + | AsyncPending + | AsyncRejected export interface FetchOptions extends AsyncOptions { defer?: boolean From 663b060710e2752cffa83706558703c4d80d418a Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 20 Sep 2019 11:15:34 +0200 Subject: [PATCH 20/47] remove unused imports --- packages/react-async/src/Async.tsx | 3 +-- packages/react-async/src/reducer.ts | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index 9f072ee6..43d73447 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -1,8 +1,7 @@ -import React, { ComponentClass } from "react" +import React from "react" import globalScope from "./globalScope" import { IfInitial, IfPending, IfFulfilled, IfRejected, IfSettled } from "./helpers" -// @ts-ignore import propTypes from "./propTypes" import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" import { diff --git a/packages/react-async/src/reducer.ts b/packages/react-async/src/reducer.ts index 742ca6c5..e41d3a24 100644 --- a/packages/react-async/src/reducer.ts +++ b/packages/react-async/src/reducer.ts @@ -1,7 +1,6 @@ import { getInitialStatus, getIdleStatus, getStatusProps, statusTypes } from "./status" import { PromiseFn, - AsyncState, AsyncAction, AsyncPending, AsyncFulfilled, From 0512a0c226622f361669605421c322f73331d8ad Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 20 Sep 2019 11:22:09 +0200 Subject: [PATCH 21/47] fix react being included in types rollup --- packages/react-async/src/types.ts | 2 ++ packages/react-async/src/useAsync.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/react-async/src/types.ts b/packages/react-async/src/types.ts index e94afbfa..72fb5985 100644 --- a/packages/react-async/src/types.ts +++ b/packages/react-async/src/types.ts @@ -1,3 +1,5 @@ +import React from "react" + export type AsyncChildren = ((state: AsyncState) => React.ReactNode) | React.ReactNode export type InitialChildren = ((state: AsyncInitial) => React.ReactNode) | React.ReactNode export type PendingChildren = ((state: AsyncPending) => React.ReactNode) | React.ReactNode diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index 95c86ae8..95a0dcef 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -1,4 +1,4 @@ -import { useCallback, useDebugValue, useEffect, useMemo, useRef, useReducer } from "react" +import React, { useCallback, useDebugValue, useEffect, useMemo, useRef, useReducer } from "react" import globalScope from "./globalScope" import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" From a9785685b9890e0720246a8764fc88cbd30dbae5 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 20 Sep 2019 11:33:12 +0200 Subject: [PATCH 22/47] fix enum names --- packages/react-async-devtools/src/index.js | 8 +++--- packages/react-async/src/Async.tsx | 10 +++---- packages/react-async/src/index.ts | 2 +- packages/react-async/src/reducer.ts | 20 +++++++------- packages/react-async/src/status.spec.js | 16 +++++------ packages/react-async/src/status.ts | 32 +++++++++++----------- packages/react-async/src/useAsync.tsx | 10 +++---- 7 files changed, 49 insertions(+), 49 deletions(-) diff --git a/packages/react-async-devtools/src/index.js b/packages/react-async-devtools/src/index.js index a5b7ebee..0989c94f 100644 --- a/packages/react-async-devtools/src/index.js +++ b/packages/react-async-devtools/src/index.js @@ -1,5 +1,5 @@ import React from "react" -import { actionTypes, reducer, globalScope } from "react-async" +import { ActionTypes, reducer, globalScope } from "react-async" import { Root, Range, Checkbox, Label, Small, Ol, Li, Button } from "./components" @@ -17,14 +17,14 @@ globalScope.__REACT_ASYNC__.devToolsDispatcher = (action, dispatch) => { state.update(action) } switch (action.type) { - case actionTypes.start: + case ActionTypes.start: if (state.intercept) { dispatch({ ...action, payload: undefined }) state.update(action, run) } else run() break - case actionTypes.fulfill: - case actionTypes.reject: + case ActionTypes.fulfill: + case ActionTypes.reject: setTimeout(run, state.latency * 1000) break default: diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index 43d73447..e01feba3 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -3,7 +3,7 @@ import React from "react" import globalScope from "./globalScope" import { IfInitial, IfPending, IfFulfilled, IfRejected, IfSettled } from "./helpers" import propTypes from "./propTypes" -import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" +import { ActionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" import { AsyncProps, AsyncState, @@ -176,7 +176,7 @@ export const createInstance = ( return (this.promise = new Promise((resolve, reject) => { if (!this.mounted) return const executor = () => promiseFn().then(resolve, reject) - this.dispatch({ type: actionTypes.start, payload: executor, meta: this.getMeta() }) + this.dispatch({ type: ActionTypes.start, payload: executor, meta: this.getMeta() }) })) } @@ -212,7 +212,7 @@ export const createInstance = ( onCancel && onCancel() this.counter++ this.abortController.abort() - this.mounted && this.dispatch({ type: actionTypes.cancel, meta: this.getMeta() }) + this.mounted && this.dispatch({ type: ActionTypes.cancel, meta: this.getMeta() }) } onResolve(counter: Number) { @@ -237,14 +237,14 @@ export const createInstance = ( setData(data: T, callback?: () => void) { this.mounted && - this.dispatch({ type: actionTypes.fulfill, payload: data, meta: this.getMeta() }, callback) + this.dispatch({ type: ActionTypes.fulfill, payload: data, meta: this.getMeta() }, callback) return data } setError(error: Error, callback?: () => void) { this.mounted && this.dispatch( - { type: actionTypes.reject, payload: error, error: true, meta: this.getMeta() }, + { type: ActionTypes.reject, payload: error, error: true, meta: this.getMeta() }, callback ) return error diff --git a/packages/react-async/src/index.ts b/packages/react-async/src/index.ts index 95d2717a..e35a81b0 100644 --- a/packages/react-async/src/index.ts +++ b/packages/react-async/src/index.ts @@ -3,7 +3,7 @@ export { default as Async, createInstance } from "./Async" export * from "./types" export { default as useAsync, useFetch, FetchOptions, FetchError } from "./useAsync" export default Async -export { statusTypes } from "./status" +export { StatusTypes } from "./status" export { default as globalScope } from "./globalScope" export * from "./helpers" export * from "./reducer" diff --git a/packages/react-async/src/reducer.ts b/packages/react-async/src/reducer.ts index e41d3a24..8ef0af83 100644 --- a/packages/react-async/src/reducer.ts +++ b/packages/react-async/src/reducer.ts @@ -1,4 +1,4 @@ -import { getInitialStatus, getIdleStatus, getStatusProps, statusTypes } from "./status" +import { getInitialStatus, getIdleStatus, getStatusProps, StatusTypes } from "./status" import { PromiseFn, AsyncAction, @@ -18,7 +18,7 @@ import { */ declare type ImportWorkaround = AbstractState -export enum actionTypes { +export enum ActionTypes { start = "start", cancel = "cancel", fulfill = "fulfill", @@ -48,16 +48,16 @@ export const init = ({ export const reducer = (state: ReducerAsyncState, action: AsyncAction) => { switch (action.type) { - case actionTypes.start: + case ActionTypes.start: return { ...state, startedAt: new Date(), finishedAt: undefined, - ...getStatusProps(statusTypes.pending), + ...getStatusProps(StatusTypes.pending), counter: action.meta.counter, promise: action.meta.promise, } as AsyncPending> - case actionTypes.cancel: + case ActionTypes.cancel: return { ...state, startedAt: undefined, @@ -69,23 +69,23 @@ export const reducer = (state: ReducerAsyncState, action: AsyncAction) | AsyncInitial> | AsyncFulfilled> | AsyncRejected> - case actionTypes.fulfill: + case ActionTypes.fulfill: return { ...state, data: action.payload, value: action.payload, error: undefined, finishedAt: new Date(), - ...getStatusProps(statusTypes.fulfilled), + ...getStatusProps(StatusTypes.fulfilled), promise: action.meta.promise, } as AsyncFulfilled> - case actionTypes.reject: + case ActionTypes.reject: return { ...state, error: action.payload, value: action.payload, finishedAt: new Date(), - ...getStatusProps(statusTypes.rejected), + ...getStatusProps(StatusTypes.rejected), promise: action.meta.promise, } as AsyncRejected> default: @@ -97,7 +97,7 @@ export const dispatchMiddleware = ( dispatch: (action: AsyncAction, ...args: any[]) => void ) => (action: AsyncAction, ...args: unknown[]) => { dispatch(action, ...args) - if (action.type === actionTypes.start && typeof action.payload === "function") { + if (action.type === ActionTypes.start && typeof action.payload === "function") { action.payload() } } diff --git a/packages/react-async/src/status.spec.js b/packages/react-async/src/status.spec.js index 16d9c7b1..f2f6fe6a 100644 --- a/packages/react-async/src/status.spec.js +++ b/packages/react-async/src/status.spec.js @@ -2,31 +2,31 @@ import "@testing-library/jest-dom/extend-expect" -import { getInitialStatus, getIdleStatus, statusTypes } from "./status" +import { getInitialStatus, getIdleStatus, StatusTypes } from "./status" describe("getInitialStatus", () => { test("returns 'initial' when given an undefined value", () => { - expect(getInitialStatus(undefined)).toEqual(statusTypes.initial) + expect(getInitialStatus(undefined)).toEqual(StatusTypes.initial) }) test("returns 'pending' when given only a promise", () => { - expect(getInitialStatus(undefined, Promise.resolve("foo"))).toEqual(statusTypes.pending) + expect(getInitialStatus(undefined, Promise.resolve("foo"))).toEqual(StatusTypes.pending) }) test("returns 'rejected' when given an Error value", () => { - expect(getInitialStatus(new Error("oops"))).toEqual(statusTypes.rejected) + expect(getInitialStatus(new Error("oops"))).toEqual(StatusTypes.rejected) }) test("returns 'fulfilled' when given any other value", () => { - expect(getInitialStatus(null)).toEqual(statusTypes.fulfilled) + expect(getInitialStatus(null)).toEqual(StatusTypes.fulfilled) }) }) describe("getIdleStatus", () => { test("returns 'initial' when given an undefined value", () => { - expect(getIdleStatus(undefined)).toEqual(statusTypes.initial) + expect(getIdleStatus(undefined)).toEqual(StatusTypes.initial) }) test("returns 'rejected' when given an Error value", () => { - expect(getIdleStatus(new Error("oops"))).toEqual(statusTypes.rejected) + expect(getIdleStatus(new Error("oops"))).toEqual(StatusTypes.rejected) }) test("returns 'fulfilled' when given any other value", () => { - expect(getIdleStatus(null)).toEqual(statusTypes.fulfilled) + expect(getIdleStatus(null)).toEqual(StatusTypes.fulfilled) }) }) diff --git a/packages/react-async/src/status.ts b/packages/react-async/src/status.ts index 2af3383b..9db4d3f4 100644 --- a/packages/react-async/src/status.ts +++ b/packages/react-async/src/status.ts @@ -1,6 +1,6 @@ import { PromiseFn } from "./types" -export enum statusTypes { +export enum StatusTypes { initial = "initial", pending = "pending", fulfilled = "fulfilled", @@ -8,25 +8,25 @@ export enum statusTypes { } export const getInitialStatus = (value?: T | Error, promise?: Promise | PromiseFn) => { - if (value instanceof Error) return statusTypes.rejected - if (value !== undefined) return statusTypes.fulfilled - if (promise) return statusTypes.pending - return statusTypes.initial + if (value instanceof Error) return StatusTypes.rejected + if (value !== undefined) return StatusTypes.fulfilled + if (promise) return StatusTypes.pending + return StatusTypes.initial } export const getIdleStatus = (value?: T | Error) => { - if (value instanceof Error) return statusTypes.rejected - if (value !== undefined) return statusTypes.fulfilled - return statusTypes.initial + if (value instanceof Error) return StatusTypes.rejected + if (value !== undefined) return StatusTypes.fulfilled + return StatusTypes.initial } -export const getStatusProps = (status: statusTypes) => ({ +export const getStatusProps = (status: StatusTypes) => ({ status, - isInitial: status === statusTypes.initial, - isPending: status === statusTypes.pending, - isLoading: status === statusTypes.pending, // alias - isFulfilled: status === statusTypes.fulfilled, - isResolved: status === statusTypes.fulfilled, // alias - isRejected: status === statusTypes.rejected, - isSettled: status === statusTypes.fulfilled || status === statusTypes.rejected, + isInitial: status === StatusTypes.initial, + isPending: status === StatusTypes.pending, + isLoading: status === StatusTypes.pending, // alias + isFulfilled: status === StatusTypes.fulfilled, + isResolved: status === StatusTypes.fulfilled, // alias + isRejected: status === StatusTypes.rejected, + isSettled: status === StatusTypes.fulfilled || status === StatusTypes.rejected, }) diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index 95a0dcef..1fd997c2 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useDebugValue, useEffect, useMemo, useRef, useReducer } from "react" import globalScope from "./globalScope" -import { actionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" +import { ActionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" import { AsyncOptions, @@ -88,7 +88,7 @@ function useAsync( (data, callback = noop) => { if (isMounted.current) { dispatch({ - type: actionTypes.fulfill, + type: ActionTypes.fulfill, payload: data, meta: getMeta(), }) @@ -103,7 +103,7 @@ function useAsync( (error, callback = noop) => { if (isMounted.current) { dispatch({ - type: actionTypes.reject, + type: ActionTypes.reject, payload: error, error: true, meta: getMeta(), @@ -138,7 +138,7 @@ function useAsync( if (!isMounted.current) return const executor = () => promiseFn().then(resolve, reject) dispatch({ - type: actionTypes.start, + type: ActionTypes.start, payload: executor, meta: getMeta(), }) @@ -185,7 +185,7 @@ function useAsync( abortController.current.abort() isMounted.current && dispatch({ - type: actionTypes.cancel, + type: ActionTypes.cancel, meta: getMeta(), }) }, [onCancel, dispatch, getMeta]) From 825c9baaf8e28681ea394d4ab3717a69d5f33af0 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 20 Sep 2019 12:26:51 +0200 Subject: [PATCH 23/47] implements MockAbortController --- packages/react-async/src/useAsync.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index 1fd997c2..288c18de 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -33,6 +33,10 @@ export interface FetchOptions extends AsyncOptions { } const noop = () => {} +class MockAbortController implements AbortController { + public abort = noop + readonly signal = {} as AbortSignal +} function useAsync(options: AsyncOptions): AsyncState function useAsync(promiseFn: PromiseFn, options?: AsyncOptions): AsyncState @@ -54,9 +58,7 @@ function useAsync( const lastArgs = useRef(undefined) const lastOptions = useRef | undefined>(undefined) const lastPromise = useRef | undefined>(undefined) - const abortController = useRef({ - abort: noop, - } as any) + const abortController = useRef(new MockAbortController()) const { devToolsDispatcher } = globalScope.__REACT_ASYNC__ const { reducer, dispatcher = devToolsDispatcher } = options From 850aab7c523dbcbe29fe17c2450b3594187ba3aa Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 20 Sep 2019 12:29:39 +0200 Subject: [PATCH 24/47] set default options --- packages/react-async/src/useAsync.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index 288c18de..2aab26f1 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -56,7 +56,7 @@ function useAsync( const counter = useRef(0) const isMounted = useRef(true) const lastArgs = useRef(undefined) - const lastOptions = useRef | undefined>(undefined) + const lastOptions = useRef>(options) const lastPromise = useRef | undefined>(undefined) const abortController = useRef(new MockAbortController()) @@ -157,7 +157,7 @@ function useAsync( .then(handleResolve(counter.current)) .catch(handleReject(counter.current)) } else if (promiseFn && !isPreInitialized) { - start(() => promiseFn(lastOptions.current!, abortController.current)) + start(() => promiseFn(lastOptions.current, abortController.current)) .then(handleResolve(counter.current)) .catch(handleReject(counter.current)) } @@ -168,7 +168,7 @@ function useAsync( (...args) => { if (deferFn) { lastArgs.current = args - start(() => deferFn(args, lastOptions.current!, abortController.current)) + start(() => deferFn(args, lastOptions.current, abortController.current)) .then(handleResolve(counter.current)) .catch(handleReject(counter.current)) } From ade7fe17481e5c79a3c4cf8114cf715185632489 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 20 Sep 2019 13:00:05 +0200 Subject: [PATCH 25/47] set types for fetch-callback --- packages/react-async/src/useAsync.tsx | 54 +++++++++++++++------------ 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index 2aab26f1..a4659f8b 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -281,32 +281,40 @@ const useAsyncFetch = ( init, isDefer, }) - const state = useAsync({ - ...options, - [fn]: useCallback( - // TODO - (arg1: any, arg2: any, arg3: any) => { - const [override, signal] = arg3 ? [arg1[0], arg3.signal] : [undefined, arg2.signal] - if (typeof override === "object" && "preventDefault" in override) { - // Don't spread Events or SyntheticEvents - return doFetch(input, { + const promiseFn = useCallback( + (_: AsyncOptions, { signal }: AbortController) => { + return doFetch(input, { + signal, + ...init, + }) + }, + [identity] // eslint-disable-line react-hooks/exhaustive-deps + ) + const deferFn = useCallback( + ([override]: any[], _: AsyncOptions, { signal }: AbortController) => { + if (typeof override === "object" && "preventDefault" in override) { + // Don't spread Events or SyntheticEvents + return doFetch(input, { + signal, + ...init, + }) + } + return typeof override === "function" + ? doFetch(input, { + signal, + ...override(init), + }) + : doFetch(input, { signal, ...init, + ...override, }) - } - return typeof override === "function" - ? doFetch(input, { - signal, - ...override(init), - }) - : doFetch(input, { - signal, - ...init, - ...override, - }) - }, - [identity] // eslint-disable-line react-hooks/exhaustive-deps - ), + }, + [identity] // eslint-disable-line react-hooks/exhaustive-deps + ) + const state = useAsync({ + ...options, + [fn]: isDefer ? deferFn : promiseFn, }) useDebugValue(state, ({ counter, status }) => `[${counter}] ${status}`) return state From ca118fe632bf29e5d2c442c708350b80f536f889 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 20 Sep 2019 14:04:43 +0200 Subject: [PATCH 26/47] Fix failing test --- packages/react-async/src/Async.spec.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/react-async/src/Async.spec.js b/packages/react-async/src/Async.spec.js index 763cc7d4..94f5b4c2 100644 --- a/packages/react-async/src/Async.spec.js +++ b/packages/react-async/src/Async.spec.js @@ -38,6 +38,7 @@ describe("Async", () => { {value => { one = value + return null }} @@ -47,6 +48,7 @@ describe("Async", () => { {value => { two = value + return null }} From 306befa84c826970e65ffca18657522ff9e15043 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 20 Sep 2019 19:57:21 +0200 Subject: [PATCH 27/47] Match propTypes to TS-types --- packages/react-async/src/helpers.spec.js | 8 +++++++- packages/react-async/src/helpers.tsx | 2 +- packages/react-async/src/propTypes.ts | 10 +++++----- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/react-async/src/helpers.spec.js b/packages/react-async/src/helpers.spec.js index 24cff478..b25aad5f 100644 --- a/packages/react-async/src/helpers.spec.js +++ b/packages/react-async/src/helpers.spec.js @@ -2,7 +2,7 @@ import "@testing-library/jest-dom/extend-expect" import React from "react" import { render, fireEvent, cleanup } from "@testing-library/react" import Async, { IfInitial, IfPending, IfFulfilled, IfRejected, IfSettled } from "./index" -import { resolveIn, resolveTo, rejectTo } from "./specs" +import { resolveIn, resolveTo, rejectTo, sleep } from "./specs" afterEach(cleanup) @@ -84,6 +84,12 @@ describe("IfFulfilled", () => { await findByText("outer inner") expect(queryByText("outer inner")).toBeInTheDocument() }) + + test("renders without children", async () => { + const promiseFn = () => resolveTo("ok") + render({state => }) + await sleep(0) + }) }) describe("IfPending", () => { diff --git a/packages/react-async/src/helpers.tsx b/packages/react-async/src/helpers.tsx index af370457..9b3d06a6 100644 --- a/packages/react-async/src/helpers.tsx +++ b/packages/react-async/src/helpers.tsx @@ -33,7 +33,7 @@ const renderFn = (children: React.ReactNode | ChildrenFn, ...args: any[]) => { const render = children as ChildrenFn return render(...args) } - return children === undefined ? null : children + return children } /** diff --git a/packages/react-async/src/propTypes.ts b/packages/react-async/src/propTypes.ts index d8fe293a..33fbea2e 100644 --- a/packages/react-async/src/propTypes.ts +++ b/packages/react-async/src/propTypes.ts @@ -46,27 +46,27 @@ export default PropTypes && { debugLabel: PropTypes.string, }, Initial: { - children: childrenFn.isRequired, + children: childrenFn, state: stateObject.isRequired, persist: PropTypes.bool, }, Pending: { - children: childrenFn.isRequired, + children: childrenFn, state: stateObject.isRequired, initial: PropTypes.bool, }, Fulfilled: { - children: childrenFn.isRequired, + children: childrenFn, state: stateObject.isRequired, persist: PropTypes.bool, }, Rejected: { - children: childrenFn.isRequired, + children: childrenFn, state: stateObject.isRequired, persist: PropTypes.bool, }, Settled: { - children: childrenFn.isRequired, + children: childrenFn, state: stateObject.isRequired, persist: PropTypes.bool, }, From c40d5d88898f9d2b6ffea87413a381af27229a3d Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Wed, 25 Sep 2019 06:52:47 +0200 Subject: [PATCH 28/47] add tests for helpers with missing state --- packages/react-async/src/helpers.spec.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/react-async/src/helpers.spec.js b/packages/react-async/src/helpers.spec.js index b25aad5f..e1c66d70 100644 --- a/packages/react-async/src/helpers.spec.js +++ b/packages/react-async/src/helpers.spec.js @@ -84,7 +84,10 @@ describe("IfFulfilled", () => { await findByText("outer inner") expect(queryByText("outer inner")).toBeInTheDocument() }) - + test("renders nothing if missing state", () => { + const { queryByText } = render(Test) + expect(queryByText("Test")).not.toBeInTheDocument() + }) test("renders without children", async () => { const promiseFn = () => resolveTo("ok") render({state => }) @@ -109,6 +112,10 @@ describe("IfPending", () => { await findByText("done") expect(queryByText("pending")).toBeNull() }) + test("renders nothing if missing state", () => { + const { queryByText } = render(Test) + expect(queryByText("Test")).not.toBeInTheDocument() + }) }) describe("IfInitial", () => { @@ -134,6 +141,10 @@ describe("IfInitial", () => { await findByText("done") expect(queryByText("pending")).toBeNull() }) + test("renders nothing if missing state", () => { + const { queryByText } = render(Test) + expect(queryByText("Test")).not.toBeInTheDocument() + }) }) describe("IfRejected", () => { @@ -148,6 +159,10 @@ describe("IfRejected", () => { await findByText("err") expect(queryByText("err")).toBeInTheDocument() }) + test("renders nothing if missing state", () => { + const { queryByText } = render(Test) + expect(queryByText("Test")).not.toBeInTheDocument() + }) }) describe("IfSettled", () => { @@ -197,4 +212,8 @@ describe("IfSettled", () => { fireEvent.click(getByText("reload")) await findByText("loading") }) + test("renders nothing if missing state", () => { + const { queryByText } = render(Test) + expect(queryByText("Test")).not.toBeInTheDocument() + }) }) From 2cb5c788a9487f1dc539df7a108cf7a28ce6afd3 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 18 Oct 2019 10:32:47 +0200 Subject: [PATCH 29/47] prevent Async context consumers to be used outside of Async components --- packages/react-async/src/Async.spec.js | 30 ++++++++++++++++++++++++++ packages/react-async/src/Async.tsx | 28 ++++++++++++++++++------ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/packages/react-async/src/Async.spec.js b/packages/react-async/src/Async.spec.js index 94f5b4c2..b45b25e4 100644 --- a/packages/react-async/src/Async.spec.js +++ b/packages/react-async/src/Async.spec.js @@ -57,6 +57,36 @@ describe("Async", () => { }) }) +describe("rendering context consumers without provider should throw an error", () => { + for (const Component of [ + Async.Initial, + Async.Pending, + Async.Fulfilled, + Async.Rejected, + Async.Settled, + ]) { + test("does not throw an error when rendered within ", () => { + expect(() => + render( + + {() => {}} + + ) + ).not.toThrowError() + }) + test("does throw an error when not rendered within ", () => { + // Prevent the thrown error from showing up in test output by mocking console.error. + jest.spyOn(console, "error") + global.console.error.mockImplementation(() => {}) + + expect(() => render({() => {}})).toThrowError() + + // Restore the original console.error so other tests will still print errors that occur. + global.console.error.mockRestore() + }) + } +}) + describe("Async.Fulfilled", () => { test("renders only after the promise is resolved", async () => { const promiseFn = () => resolveTo("ok") diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index e01feba3..a7a21f0a 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -66,7 +66,23 @@ export const createInstance = ( defaultProps: AsyncProps = {}, displayName = "Async" ): AsyncConstructor => { - const { Consumer, Provider } = React.createContext>(undefined as any) + const { Consumer: UnguardedConsumer, Provider } = React.createContext | undefined>( + undefined + ) + function Consumer({ children }: { children: (value: AsyncState) => React.ReactNode }) { + return ( + + {value => { + if (!value) { + throw new Error( + "this component should only be used within an associated component!" + ) + } + return children(value) + }} + + ) + } type Props = AsyncProps @@ -266,19 +282,19 @@ export const createInstance = ( if (propTypes) (Async as React.ComponentClass).propTypes = propTypes.Async const AsyncInitial: AsyncConstructor["Initial"] = props => ( - {(st: AsyncState) => } + {st => } ) const AsyncPending: AsyncConstructor["Pending"] = props => ( - {(st: AsyncState) => } + {st => } ) const AsyncFulfilled: AsyncConstructor["Fulfilled"] = props => ( - {(st: AsyncState) => } + {st => } ) const AsyncRejected: AsyncConstructor["Rejected"] = props => ( - {(st: AsyncState) => } + {st => } ) const AsyncSettled: AsyncConstructor["Settled"] = props => ( - {(st: AsyncState) => } + {st => } ) AsyncInitial.displayName = `${displayName}.Initial` From ef8deff2336d73437a5c86bab8a10a53d9192f3f Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 18 Oct 2019 10:36:33 +0200 Subject: [PATCH 30/47] alias AsyncState & AsyncConstructor for readability --- packages/react-async/src/Async.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index a7a21f0a..01fdd689 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -85,8 +85,10 @@ export const createInstance = ( } type Props = AsyncProps + type State = AsyncState + type Constructor = AsyncConstructor - class Async extends React.Component> { + class Async extends React.Component { private mounted = false private counter = 0 private args: any[] = [] @@ -108,7 +110,7 @@ export const createInstance = ( this.setError = this.setError.bind(this) const promise = props.promise - const promiseFn = props.promiseFn || defaultProps.promiseFn + const promiseFn = props.promiseFn || defaultProps.promiseFConstructorn const initialValue = props.initialValue || defaultProps.initialValue this.state = { @@ -269,7 +271,7 @@ export const createInstance = ( render() { const { children } = this.props if (typeof children === "function") { - const render = children as (state: AsyncState) => React.ReactNode + const render = children as (state: State) => React.ReactNode return {render(this.state)} } if (children !== undefined && children !== null) { @@ -281,19 +283,19 @@ export const createInstance = ( if (propTypes) (Async as React.ComponentClass).propTypes = propTypes.Async - const AsyncInitial: AsyncConstructor["Initial"] = props => ( + const AsyncInitial: Constructor["Initial"] = props => ( {st => } ) - const AsyncPending: AsyncConstructor["Pending"] = props => ( + const AsyncPending: Constructor["Pending"] = props => ( {st => } ) - const AsyncFulfilled: AsyncConstructor["Fulfilled"] = props => ( + const AsyncFulfilled: Constructor["Fulfilled"] = props => ( {st => } ) - const AsyncRejected: AsyncConstructor["Rejected"] = props => ( + const AsyncRejected: Constructor["Rejected"] = props => ( {st => } ) - const AsyncSettled: AsyncConstructor["Settled"] = props => ( + const AsyncSettled: Constructor["Settled"] = props => ( {st => } ) From 8ccfc0fdf8f859faec33daa0f4695edaaedda287 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Fri, 18 Oct 2019 10:54:39 +0200 Subject: [PATCH 31/47] move MockAbortController out and also use it in Async component --- packages/react-async/src/Async.tsx | 4 ++-- packages/react-async/src/globalScope.ts | 6 ++++++ packages/react-async/src/useAsync.tsx | 8 +------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index 01fdd689..9818bc1d 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -1,6 +1,6 @@ import React from "react" -import globalScope from "./globalScope" +import globalScope, { MockAbortController } from "./globalScope" import { IfInitial, IfPending, IfFulfilled, IfRejected, IfSettled } from "./helpers" import propTypes from "./propTypes" import { ActionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" @@ -93,7 +93,7 @@ export const createInstance = ( private counter = 0 private args: any[] = [] private promise?: Promise = undefined - private abortController: AbortController = { abort: () => {} } as any + private abortController: AbortController = new MockAbortController() private debugLabel?: string private dispatch: (action: AsyncAction, ...args: any[]) => void diff --git a/packages/react-async/src/globalScope.ts b/packages/react-async/src/globalScope.ts index 5804b37d..8fd3ffbd 100644 --- a/packages/react-async/src/globalScope.ts +++ b/packages/react-async/src/globalScope.ts @@ -22,4 +22,10 @@ const globalScope = (() => { */ globalScope.__REACT_ASYNC__ = globalScope.__REACT_ASYNC__ || {} +export const noop = () => {} +export class MockAbortController implements AbortController { + public abort = noop + readonly signal = {} as AbortSignal +} + export default globalScope diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index a4659f8b..ea29312e 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useDebugValue, useEffect, useMemo, useRef, useReducer } from "react" -import globalScope from "./globalScope" +import globalScope, { MockAbortController, noop } from "./globalScope" import { ActionTypes, init, dispatchMiddleware, reducer as asyncReducer } from "./reducer" import { @@ -32,12 +32,6 @@ export interface FetchOptions extends AsyncOptions { json?: boolean } -const noop = () => {} -class MockAbortController implements AbortController { - public abort = noop - readonly signal = {} as AbortSignal -} - function useAsync(options: AsyncOptions): AsyncState function useAsync(promiseFn: PromiseFn, options?: AsyncOptions): AsyncState From e95a542a5db59b1531aa11d80a50e06b950a3ae7 Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 18 Oct 2019 11:22:59 +0200 Subject: [PATCH 32/47] =?UTF-8?q?Revert=20"Allow=20overriding=20the=20'res?= =?UTF-8?q?ource'=20argument=20of=20'fetch'=20when=20invokin=E2=80=A6=20(#?= =?UTF-8?q?150)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This only reverts code changes to redo them manually in TS --- packages/react-async/src/useAsync.js | 86 ++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/packages/react-async/src/useAsync.js b/packages/react-async/src/useAsync.js index a43c31c5..38a55c3d 100644 --- a/packages/react-async/src/useAsync.js +++ b/packages/react-async/src/useAsync.js @@ -12,14 +12,22 @@ import { const noop = () => {} const useAsync = (arg1, arg2) => { - const options = typeof arg1 === "function" ? { ...arg2, promiseFn: arg1 } : arg1 + const options = + typeof arg1 === "function" + ? { + ...arg2, + promiseFn: arg1, + } + : arg1 const counter = useRef(0) const isMounted = useRef(true) const lastArgs = useRef(undefined) const lastOptions = useRef(undefined) const lastPromise = useRef(neverSettle) - const abortController = useRef({ abort: noop }) + const abortController = useRef({ + abort: noop, + }) const { devToolsDispatcher } = globalScope.__REACT_ASYNC__ const { reducer, dispatcher = devToolsDispatcher } = options @@ -37,14 +45,23 @@ const useAsync = (arg1, arg2) => { const { debugLabel } = options const getMeta = useCallback( - meta => ({ counter: counter.current, promise: lastPromise.current, debugLabel, ...meta }), + meta => ({ + counter: counter.current, + promise: lastPromise.current, + debugLabel, + ...meta, + }), [debugLabel] ) const setData = useCallback( (data, callback = noop) => { if (isMounted.current) { - dispatch({ type: actionTypes.fulfill, payload: data, meta: getMeta() }) + dispatch({ + type: actionTypes.fulfill, + payload: data, + meta: getMeta(), + }) callback() } return data @@ -55,7 +72,12 @@ const useAsync = (arg1, arg2) => { const setError = useCallback( (error, callback = noop) => { if (isMounted.current) { - dispatch({ type: actionTypes.reject, payload: error, error: true, meta: getMeta() }) + dispatch({ + type: actionTypes.reject, + payload: error, + error: true, + meta: getMeta(), + }) callback() } return error @@ -83,7 +105,11 @@ const useAsync = (arg1, arg2) => { return (lastPromise.current = new Promise((resolve, reject) => { if (!isMounted.current) return const executor = () => promiseFn().then(resolve, reject) - dispatch({ type: actionTypes.start, payload: executor, meta: getMeta() }) + dispatch({ + type: actionTypes.start, + payload: executor, + meta: getMeta(), + }) })) }, [dispatch, getMeta] @@ -125,7 +151,11 @@ const useAsync = (arg1, arg2) => { onCancel && onCancel() counter.current++ abortController.current.abort() - isMounted.current && dispatch({ type: actionTypes.cancel, meta: getMeta() }) + isMounted.current && + dispatch({ + type: actionTypes.cancel, + meta: getMeta(), + }) }, [onCancel, dispatch, getMeta]) /* These effects should only be triggered on changes to specific props */ @@ -183,31 +213,41 @@ const parseResponse = (accept, json) => res => { return accept === "application/json" ? res.json() : res } -const useAsyncFetch = (resource, init, { defer, json, ...options } = {}) => { - const method = resource.method || (init && init.method) - const headers = resource.headers || (init && init.headers) || {} +const useAsyncFetch = (input, init, { defer, json, ...options } = {}) => { + const method = input.method || (init && init.method) + const headers = input.headers || (init && init.headers) || {} const accept = headers["Accept"] || headers["accept"] || (headers.get && headers.get("accept")) - const doFetch = (resource, init) => - globalScope.fetch(resource, init).then(parseResponse(accept, json)) + const doFetch = (input, init) => globalScope.fetch(input, init).then(parseResponse(accept, json)) const isDefer = typeof defer === "boolean" ? defer : ["POST", "PUT", "PATCH", "DELETE"].indexOf(method) !== -1 const fn = isDefer ? "deferFn" : "promiseFn" - const identity = JSON.stringify({ resource, init, isDefer }) + const identity = JSON.stringify({ + input, + init, + isDefer, + }) const state = useAsync({ ...options, [fn]: useCallback( (arg1, arg2, arg3) => { - const [override, signal] = isDefer ? [arg1[0], arg3.signal] : [undefined, arg2.signal] - const isEvent = typeof override === "object" && "preventDefault" in override - if (!override || isEvent) { - return doFetch(resource, { signal, ...init }) - } - if (typeof override === "function") { - const { resource: runResource, ...runInit } = override({ resource, signal, ...init }) - return doFetch(runResource || resource, { signal, ...runInit }) + const [override, signal] = arg3 ? [arg1[0], arg3.signal] : [undefined, arg2.signal] + if (typeof override === "object" && "preventDefault" in override) { + // Don't spread Events or SyntheticEvents + return doFetch(input, { + signal, + ...init, + }) } - const { resource: runResource, ...runInit } = override - return doFetch(runResource || resource, { signal, ...init, ...runInit }) + return typeof override === "function" + ? doFetch(input, { + signal, + ...override(init), + }) + : doFetch(input, { + signal, + ...init, + ...override, + }) }, [identity] // eslint-disable-line react-hooks/exhaustive-deps ), From bf9d08a72acf96078c9c07d42066f0fc8ef6afbc Mon Sep 17 00:00:00 2001 From: Thomas Gnandt Date: Fri, 18 Oct 2019 12:45:07 +0200 Subject: [PATCH 33/47] Reapply #150 --- packages/react-async/src/useAsync.tsx | 58 ++++++++++++++------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/packages/react-async/src/useAsync.tsx b/packages/react-async/src/useAsync.tsx index d387c1ea..dc86bfb9 100644 --- a/packages/react-async/src/useAsync.tsx +++ b/packages/react-async/src/useAsync.tsx @@ -254,29 +254,42 @@ const parseResponse = (accept: undefined | string, json: undefined | boolean) => return accept === "application/json" ? res.json() : res } +type OverrideParams = { resource?: RequestInfo } & Partial + interface FetchRun extends Omit, "run"> { - run(overrideInit: (init: RequestInit) => RequestInit): void - run(overrideInit: Partial): void + run(overrideParams: (params?: OverrideParams) => OverrideParams): void + run(overrideParams: OverrideParams): void run(ignoredEvent: React.SyntheticEvent): void run(ignoredEvent: Event): void run(): void } +type FetchRunArgs = + | [(params?: OverrideParams) => OverrideParams] + | [OverrideParams] + | [React.SyntheticEvent] + | [Event] + | [] + +function isEvent(e: FetchRunArgs[0]): e is Event | React.SyntheticEvent { + return typeof e === "object" && "preventDefault" in e +} + /** * - * @param {RequestInfo} input + * @param {RequestInfo} resource * @param {RequestInit} init * @param {FetchOptions} options * @returns {AsyncState>} */ const useAsyncFetch = ( - input: RequestInfo, + resource: RequestInfo, init: RequestInit, { defer, json, ...options }: FetchOptions = {} ): AsyncState> => { - const method = (input as Request).method || (init && init.method) + const method = (resource as Request).method || (init && init.method) const headers: Headers & Record = - (input as Request).headers || (init && init.headers) || {} + (resource as Request).headers || (init && init.headers) || {} const accept: string | undefined = headers["Accept"] || headers["accept"] || (headers.get && headers.get("accept")) const doFetch = (input: RequestInfo, init: RequestInit) => @@ -285,38 +298,27 @@ const useAsyncFetch = ( typeof defer === "boolean" ? defer : ["POST", "PUT", "PATCH", "DELETE"].indexOf(method!) !== -1 const fn = isDefer ? "deferFn" : "promiseFn" const identity = JSON.stringify({ - input, + resource, init, isDefer, }) const promiseFn = useCallback( (_: AsyncOptions, { signal }: AbortController) => { - return doFetch(input, { - signal, - ...init, - }) + return doFetch(resource, { signal, ...init }) }, [identity] // eslint-disable-line react-hooks/exhaustive-deps ) const deferFn = useCallback( - ([override]: any[], _: AsyncOptions, { signal }: AbortController) => { - if (typeof override === "object" && "preventDefault" in override) { - // Don't spread Events or SyntheticEvents - return doFetch(input, { - signal, - ...init, - }) + function([override]: FetchRunArgs, _: AsyncOptions, { signal }: AbortController) { + if (!override || isEvent(override)) { + return doFetch(resource, { signal, ...init }) + } + if (typeof override === "function") { + const { resource: runResource, ...runInit } = override({ resource, signal, ...init }) + return doFetch(runResource || resource, { signal, ...runInit }) } - return typeof override === "function" - ? doFetch(input, { - signal, - ...override(init), - }) - : doFetch(input, { - signal, - ...init, - ...override, - }) + const { resource: runResource, ...runInit } = override + return doFetch(runResource || resource, { signal, ...init, ...runInit }) }, [identity] // eslint-disable-line react-hooks/exhaustive-deps ) From f6245df25b180de685533d36ce5f5a23a017f201 Mon Sep 17 00:00:00 2001 From: Khartir Date: Mon, 21 Oct 2019 07:12:15 +0200 Subject: [PATCH 34/47] fix typo --- packages/react-async/src/Async.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-async/src/Async.tsx b/packages/react-async/src/Async.tsx index b50e4fbf..6f7e8971 100644 --- a/packages/react-async/src/Async.tsx +++ b/packages/react-async/src/Async.tsx @@ -116,7 +116,7 @@ export const createInstance = ( this.setError = this.setError.bind(this) const promise = props.promise - const promiseFn = props.promiseFn || defaultOptions.promiseFConstructorn + const promiseFn = props.promiseFn || defaultOptions.promiseFn const initialValue = props.initialValue || defaultOptions.initialValue this.state = { From bce77950f205fc879ff253affdb99b81e8fbb2a2 Mon Sep 17 00:00:00 2001 From: Khartir Date: Mon, 21 Oct 2019 07:12:22 +0200 Subject: [PATCH 35/47] cleanup config --- packages/react-async/tsconfig.json | 60 +----------------------------- 1 file changed, 2 insertions(+), 58 deletions(-) diff --git a/packages/react-async/tsconfig.json b/packages/react-async/tsconfig.json index 54bd4795..f261ce40 100644 --- a/packages/react-async/tsconfig.json +++ b/packages/react-async/tsconfig.json @@ -1,70 +1,14 @@ { "compilerOptions": { - /* Basic Options */ "target": "es2019", - /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "esnext", - /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - // "lib": [], /* Specify library files to be included in the compilation. */ "allowJs": false, - /* Allow javascript files to be compiled. */ "checkJs": false, - /* Report errors in .js files. */ "jsx": "react", - /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ "declaration": true, - /* Generates corresponding '.d.ts' file. */ - // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ - // "sourceMap": true, /* Generates corresponding '.map' file. */ - // "outFile": "./", /* Concatenate and emit output to single file. */ - // "outDir": "./", /* Redirect output structure to the directory. */ - // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - // "composite": true, /* Enable project compilation */ - // "incremental": true, /* Enable incremental compilation */ - // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ - // "removeComments": true, /* Do not emit comments to output. */ - // "noEmit": true, /* Do not emit outputs. */ - // "importHelpers": true, /* Import emit helpers from 'tslib'. */ - // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ - // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ "skipLibCheck": true, - /* Strict Type-Checking Options */ "strict": true, - /* Enable all strict type-checking options. */ - // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - // "strictNullChecks": true, /* Enable strict null checks. */ - // "strictFunctionTypes": true, /* Enable strict checking of function types. */ - // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - - /* Additional Checks */ - // "noUnusedLocals": true, /* Report errors on unused locals. */ - // "noUnusedParameters": true, /* Report errors on unused parameters. */ - // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ - - /* Module Resolution Options */ - // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ - // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ - // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ - // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ - // "typeRoots": [], /* List of folders to include type definitions from. */ - // "types": [], /* Type declaration files to be included in compilation. */ - // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ - "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ - - /* Source Map Options */ - // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ - // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ - // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ - // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ - - /* Experimental Options */ - // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ - // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + "esModuleInterop": true }, "include": ["src"] -} \ No newline at end of file +} From 8bbcec401f31a1a8ad883d1d8d394d2735b4bf15 Mon Sep 17 00:00:00 2001 From: Khartir Date: Mon, 21 Oct 2019 07:12:31 +0200 Subject: [PATCH 36/47] remove yarn.lock --- packages/react-async/yarn.lock | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 packages/react-async/yarn.lock diff --git a/packages/react-async/yarn.lock b/packages/react-async/yarn.lock deleted file mode 100644 index 8dfa8fd0..00000000 --- a/packages/react-async/yarn.lock +++ /dev/null @@ -1,8 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -typescript@^3.6.2: - version "3.6.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54" - integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw== From 4e04af2afe96f5e43ed9fcf4a19453de8e45f59f Mon Sep 17 00:00:00 2001 From: Khartir Date: Mon, 21 Oct 2019 07:13:23 +0200 Subject: [PATCH 37/47] remove TS-dependency in package --- packages/react-async/package.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/react-async/package.json b/packages/react-async/package.json index f9a30d66..9559c4ee 100644 --- a/packages/react-async/package.json +++ b/packages/react-async/package.json @@ -41,8 +41,5 @@ "@pika/plugin-bundle-types" ] ] - }, - "devDependencies": { - "typescript": "^3.6.2" } -} \ No newline at end of file +} From 7d558813fdd22994fb43094c497c22da5a97f72f Mon Sep 17 00:00:00 2001 From: Khartir Date: Wed, 23 Oct 2019 17:07:12 +0200 Subject: [PATCH 38/47] remove resolutions --- package.json | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/package.json b/package.json index d553d7a1..c966d21b 100644 --- a/package.json +++ b/package.json @@ -75,12 +75,5 @@ "react-async": "^8.0.0-alpha.0", "react-dom": "16.9.0", "storybook-chromatic": "2.2.2" - }, - "resolutions": { - "typescript": "3.6.3", - "react": "16.10.2", - "react-async": "8.0.0", - "react-dom": "16.10.2", - "storybook-chromatic": "3.0.3" } -} \ No newline at end of file +} From 3dc3fc19fe5277d4fcbd3988fd8d9eb6e9b26f55 Mon Sep 17 00:00:00 2001 From: Khartir Date: Wed, 23 Oct 2019 17:11:41 +0200 Subject: [PATCH 39/47] update pika-packages --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index c966d21b..5894ffd4 100644 --- a/package.json +++ b/package.json @@ -43,12 +43,12 @@ "@babel/preset-react": "7.6.3", "@babel/preset-typescript": "^7.3.3", "@pika/pack": "0.5.0", - "@pika/plugin-build-node": "0.6.1", - "@pika/plugin-build-types": "0.6.1", - "@pika/plugin-build-web": "0.6.1", - "@pika/plugin-bundle-types": "^0.6.1", - "@pika/plugin-standard-pkg": "0.6.1", - "@pika/plugin-ts-standard-pkg": "^0.6.1", + "@pika/plugin-build-node": "0.7.0", + "@pika/plugin-build-types": "0.7.0", + "@pika/plugin-build-web": "0.7.0", + "@pika/plugin-bundle-types": "0.7.0", + "@pika/plugin-standard-pkg": "0.7.0", + "@pika/plugin-ts-standard-pkg": "0.7.0", "@storybook/react": "5.2.4", "@testing-library/jest-dom": "4.1.2", "@testing-library/react": "9.3.0", From 372c5369583d0daaf272934ec22e2527e93c4c72 Mon Sep 17 00:00:00 2001 From: Khartir Date: Wed, 23 Oct 2019 17:28:38 +0200 Subject: [PATCH 40/47] update dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 5894ffd4..c6656b69 100644 --- a/package.json +++ b/package.json @@ -71,9 +71,9 @@ "npm-run-all": "4.1.5", "prettier": "1.18.2", "prop-types": "15.7.2", - "react": "16.9.0", - "react-async": "^8.0.0-alpha.0", - "react-dom": "16.9.0", - "storybook-chromatic": "2.2.2" + "react-async": "8.0.0", + "react": "16.10.2", + "react-dom": "16.10.2", + "storybook-chromatic": "3.0.3" } } From 326b3bd9ca771adffea0b8298ee117e79d64ca33 Mon Sep 17 00:00:00 2001 From: Khartir Date: Wed, 23 Oct 2019 17:35:29 +0200 Subject: [PATCH 41/47] fix test --- packages/react-async/src/Async.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-async/src/Async.spec.js b/packages/react-async/src/Async.spec.js index dd81961c..cb00a41d 100644 --- a/packages/react-async/src/Async.spec.js +++ b/packages/react-async/src/Async.spec.js @@ -69,7 +69,7 @@ describe("rendering context consumers without provider should throw an error", ( expect(() => render( - {() => {}} + {() => null} ) ).not.toThrowError() @@ -79,7 +79,7 @@ describe("rendering context consumers without provider should throw an error", ( jest.spyOn(console, "error") global.console.error.mockImplementation(() => {}) - expect(() => render({() => {}})).toThrowError() + expect(() => render({() => null})).toThrowError() // Restore the original console.error so other tests will still print errors that occur. global.console.error.mockRestore() From caf3b5d4809befdeae342093e3b4ba600ed5c7b5 Mon Sep 17 00:00:00 2001 From: Khartir Date: Wed, 23 Oct 2019 17:36:48 +0200 Subject: [PATCH 42/47] remove dependency of react-async to fix chromatic --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index c6656b69..7f3b4d3e 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,6 @@ "npm-run-all": "4.1.5", "prettier": "1.18.2", "prop-types": "15.7.2", - "react-async": "8.0.0", "react": "16.10.2", "react-dom": "16.10.2", "storybook-chromatic": "3.0.3" From c23f462eb9113d0165eb795debf29d008cf4eb2a Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Sat, 26 Oct 2019 15:49:56 +0200 Subject: [PATCH 43/47] add typescript as a dev dependency --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index d6b967f4..7655dc17 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "prop-types": "15.7.2", "react": "^16.11.0", "react-dom": "^16.11.0", - "storybook-chromatic": "3.0.3" + "storybook-chromatic": "3.0.3", + "typescript": "3.6.4" } } From 46bbabb9dcca823ee8058aae1b9a19194e595b08 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Sat, 26 Oct 2019 16:18:06 +0200 Subject: [PATCH 44/47] fix resolutions for tests that switch react versions --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7655dc17..f99d58fa 100644 --- a/package.json +++ b/package.json @@ -20,12 +20,13 @@ "test:watch": "yarn test -- --watch", "test:devtools": "jest react-async-devtools/src", "test:components": "jest src/Async.spec.js --collectCoverageFrom=src/Async.js", - "test:backwards": "yarn add -D -W react@16.3.1 react-dom@16.3.1 && yarn test:components", - "test:forwards": "yarn add -D -W react@next react-dom@next && yarn test", - "test:latest": "yarn add -D -W react@latest react-dom@latest && yarn test", + "test:backwards": "yarn add -D -W react@16.3.1 react-dom@16.3.1 && yarn resolutions:fix-react && yarn test:components", + "test:forwards": "yarn add -D -W react@next react-dom@next && yarn resolutions:fix-react && yarn test", + "test:latest": "yarn add -D -W react@latest react-dom@latest && yarn resolutions:fix-react && yarn test", "test:compat": "yarn test:backwards && yarn test:forwards && yarn test:latest", "test:examples": "CI=1 lerna run --scope '*-example' test -- --passWithNoTests --watchAll=false", "test:chromatic": "chromatic --app-code iiua39bmt0j --build-script-name build:storybook", + "resolutions:fix-react": "jq '.resolutions.react = .devDependencies.react|.resolutions.\"react-dom\"=.devDependencies.react' package.json > package.json.new; mv package.json.new package.json; yarn install", "ci": "yarn lint && yarn test:compat && yarn test:examples", "build:packages": "lerna run --scope 'react-async*' build", "build:examples": "lerna run --scope '*-example' build", @@ -67,6 +68,7 @@ "eslint-plugin-react-hooks": "2.2.0", "jest": "24.9.0", "lerna": "3.18.3", + "node-jq": "1.10.3", "now": "16.4.3", "npm-run-all": "4.1.5", "prettier": "1.18.2", From 638a1ee623f1454004b77da314413dbb77c6c937 Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Mon, 28 Oct 2019 21:34:53 +0100 Subject: [PATCH 45/47] add umd build, add fields for module, unpkgs, jsdelivr to final package.json --- package.json | 1 + packages/react-async/package.json | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f99d58fa..dcc46e8d 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "@pika/pack": "0.5.0", "@pika/plugin-build-node": "0.7.1", "@pika/plugin-build-types": "0.7.1", + "@pika/plugin-build-umd": "^0.7.1", "@pika/plugin-build-web": "0.7.1", "@pika/plugin-bundle-types": "0.7.1", "@pika/plugin-standard-pkg": "0.7.1", diff --git a/packages/react-async/package.json b/packages/react-async/package.json index 29c08ec1..c88d35dd 100644 --- a/packages/react-async/package.json +++ b/packages/react-async/package.json @@ -35,7 +35,17 @@ "@pika/plugin-build-node" ], [ - "@pika/plugin-build-web" + "@pika/plugin-build-web", + { + "entrypoint": [ + "module", + "unpkg", + "jsdelivr" + ] + } + ], + [ + "@pika/plugin-build-umd" ], [ "@pika/plugin-bundle-types" From 3bc18ced489913763a3b635ea4c9aafe791b55af Mon Sep 17 00:00:00 2001 From: Lenz Weber Date: Wed, 30 Oct 2019 19:06:32 +0100 Subject: [PATCH 46/47] Apply suggestions from code review Co-Authored-By: Gert Hengeveld --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 66f9089e..bb23db95 100644 --- a/package.json +++ b/package.json @@ -42,11 +42,11 @@ "@babel/plugin-transform-runtime": "7.6.2", "@babel/preset-env": "7.6.3", "@babel/preset-react": "7.6.3", - "@babel/preset-typescript": "^7.3.3", + "@babel/preset-typescript": "7.3.3", "@pika/pack": "0.5.0", "@pika/plugin-build-node": "0.7.1", "@pika/plugin-build-types": "0.7.1", - "@pika/plugin-build-umd": "^0.7.1", + "@pika/plugin-build-umd": "0.7.1", "@pika/plugin-build-web": "0.7.1", "@pika/plugin-bundle-types": "0.7.1", "@pika/plugin-standard-pkg": "0.7.1", @@ -54,8 +54,8 @@ "@storybook/react": "5.2.5", "@testing-library/jest-dom": "4.2.0", "@testing-library/react": "9.3.0", - "@typescript-eslint/eslint-plugin": "^2.1.0", - "@typescript-eslint/parser": "^2.1.0", + "@typescript-eslint/eslint-plugin": "2.1.0", + "@typescript-eslint/parser": "2.1.0", "babel-eslint": "10.0.3", "babel-jest": "24.9.0", "babel-loader": "8.0.6", From 9a505438194c177b00c42a58cd75d898e16c9d70 Mon Sep 17 00:00:00 2001 From: Gert Hengeveld Date: Sun, 10 Nov 2019 11:41:47 +0100 Subject: [PATCH 47/47] Bump dependencies. --- examples/with-typescript/package.json | 2 +- package.json | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/with-typescript/package.json b/examples/with-typescript/package.json index 1b7ce772..d82ce02a 100644 --- a/examples/with-typescript/package.json +++ b/examples/with-typescript/package.json @@ -14,7 +14,7 @@ "now-build": "SKIP_PREFLIGHT_CHECK=true react-scripts build" }, "dependencies": { - "@types/node": "12.12.6", + "@types/node": "12.12.7", "@types/react": "16.9.11", "@types/react-dom": "16.9.4", "react": "16.11.0", diff --git a/package.json b/package.json index 556ba532..1d400de2 100644 --- a/package.json +++ b/package.json @@ -37,12 +37,12 @@ }, "devDependencies": { "@babel/core": "7.7.2", - "@babel/plugin-proposal-class-properties": "7.5.5", + "@babel/plugin-proposal-class-properties": "7.7.0", "@babel/plugin-proposal-object-rest-spread": "7.6.2", "@babel/plugin-transform-runtime": "7.6.2", "@babel/preset-env": "7.7.1", "@babel/preset-react": "7.7.0", - "@babel/preset-typescript": "7.3.3", + "@babel/preset-typescript": "7.7.2", "@pika/pack": "0.5.0", "@pika/plugin-build-node": "0.7.1", "@pika/plugin-build-types": "0.7.1", @@ -54,8 +54,8 @@ "@storybook/react": "5.2.6", "@testing-library/jest-dom": "4.2.3", "@testing-library/react": "9.3.2", - "@typescript-eslint/eslint-plugin": "2.1.0", - "@typescript-eslint/parser": "2.1.0", + "@typescript-eslint/eslint-plugin": "2.6.1", + "@typescript-eslint/parser": "2.6.1", "babel-eslint": "10.0.3", "babel-jest": "24.9.0", "babel-loader": "8.0.6", @@ -69,7 +69,7 @@ "eslint-plugin-react-hooks": "2.2.0", "jest": "24.9.0", "lerna": "3.18.4", - "node-jq": "1.10.3", + "node-jq": "1.11.0", "now": "16.4.4", "npm-run-all": "4.1.5", "prettier": "1.19.1", @@ -78,6 +78,6 @@ "react-async": "9.0.0", "react-dom": "16.11.0", "storybook-chromatic": "3.1.0", - "typescript": "3.6.4" + "typescript": "3.7.2" } }