From 77c2c5d8361937d55435865fdbe73dccbdc55900 Mon Sep 17 00:00:00 2001 From: JeffJiang Date: Fri, 6 Sep 2019 23:13:33 +0800 Subject: [PATCH] feat: supports effect action (#53) * feat(core): supports effect action supports effect action and change interceptor to middleware * test: rewrite some test with react-hooks-testing-library * chore: remove unexpect adding dependency * feat: use the method's name as the default effect key * fix: eslint * docs: update README --- .eslintrc.js | 1 + README.md | 27 ++- example/index.tsx | 39 ++- example/src/components/Todo/index.tsx | 6 +- example/src/models/CounterModel.ts | 7 +- example/src/models/TodoModel.ts | 3 +- example/src/services/TodoService.ts | 2 +- package.json | 7 +- src/core/AutoBind.ts | 86 +++++++ src/core/EffectContext.ts | 54 ----- src/core/EffectEvent.ts | 18 ++ src/core/StatedBeanApplication.ts | 62 +---- src/core/StatedBeanContainer.ts | 66 ++--- src/core/index.ts | 3 +- src/decorator/Effect.ts | 60 +++++ src/decorator/StatedBean.ts | 3 +- src/decorator/index.ts | 1 + src/hooks/index.ts | 1 + src/hooks/useBean.ts | 4 +- src/hooks/useInject.ts | 23 +- src/hooks/useObserveEffect.ts | 42 ++++ src/index.ts | 2 +- src/interceptor/StatedInterceptor.ts | 13 - src/interceptor/index.ts | 1 - src/middleware/EffectMiddleware.ts | 8 + src/middleware/index.ts | 1 + src/types/EffectEventValue.ts | 13 + src/types/index.ts | 1 + src/utils/index.ts | 4 + test/application.test.ts | 42 +--- test/effect-action.test.ts | 36 +++ test/post-provided.test.ts | 22 +- test/stated-change.test.tsx | 51 ++-- test/use-bean.test.tsx | 33 +-- test/use-inject.test.tsx | 53 ++++ test/utils.ts | 7 + yarn.lock | 336 ++------------------------ 37 files changed, 514 insertions(+), 624 deletions(-) create mode 100644 src/core/AutoBind.ts delete mode 100644 src/core/EffectContext.ts create mode 100644 src/core/EffectEvent.ts create mode 100644 src/decorator/Effect.ts create mode 100644 src/hooks/useObserveEffect.ts delete mode 100644 src/interceptor/StatedInterceptor.ts delete mode 100644 src/interceptor/index.ts create mode 100644 src/middleware/EffectMiddleware.ts create mode 100644 src/middleware/index.ts create mode 100644 src/types/EffectEventValue.ts create mode 100644 test/effect-action.test.ts create mode 100644 test/use-inject.test.tsx create mode 100644 test/utils.ts diff --git a/.eslintrc.js b/.eslintrc.js index 5f484876..097bd31b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -10,6 +10,7 @@ module.exports = { '@typescript-eslint/interface-name-prefix': 0, '@typescript-eslint/no-floating-promises': 0, '@typescript-eslint/no-type-alias': 0, + '@typescript-eslint/unbound-method': 0, 'react/jsx-handler-names': 0, }, }, diff --git a/README.md b/README.md index 23181e7e..96fb80a2 100644 --- a/README.md +++ b/README.md @@ -85,13 +85,17 @@ Indicates that an annotated property is `Stated`. Its reassignment will be obser The `PostProvided` decorator is used on a method that needs to be executed after the `StatedBean` be instanced to perform any initialization. +#### `@Effect(name?: string | symbol): MethodDecorator` + +The `Effect` decorator is used on a method that can get the execution state by `useObserveEffect`. + ### use Hooks #### `useBean(typeOrSupplier: ClassType | () => T, name?: string | symbol): T` The `useBean` will create an instance of the stated bean with a new `StatedBeanContainer` and listen for its data changes to trigger the re-rendering of the current component. -### `useInject(type: ClassType, option: UseStatedBeanOption = {}): T` +#### `useInject(type: ClassType, option: UseStatedBeanOption = {}): T` The `useInject` will get the instance of the stated bean from the `StatedBeanContainer` in the context and listen for its data changes to trigger the re-rendering of the current component. @@ -133,6 +137,27 @@ option = { }; ``` +#### `useObserveEffect(bean: StatedBeanType, name: string | symbol): EffectAction` + +observe the execution state of the method which with `@Effect`. + +```tsx +@StatedBean +class UserModel { + @Effect() + fetchUser() { + // ... + } +} + +const UserInfo = () => { + const model = useBean(() => new UserModel()); + const { loading, error } = useObserveEffect(model, 'fetchUser'); + + return; //...; +}; +``` + ### Provider #### `` diff --git a/example/index.tsx b/example/index.tsx index 76cbf309..e9fc1187 100644 --- a/example/index.tsx +++ b/example/index.tsx @@ -1,21 +1,20 @@ import '@abraham/reflection'; -import { ReflectiveInjector } from 'injection-js'; -import { - StatedBeanProvider, - StatedBeanApplication, - IBeanFactory, - EffectContext, - StatedInterceptor, - NextCaller, - useBean, -} from '../src'; +import { ReflectiveInjector } from 'injection-js'; import { Counter } from './src/components/Counter'; import { TodoApp } from './src/components/Todo'; import { TodoModel } from './src/models/TodoModel'; import { TodoService } from './src/services/TodoService'; +import { + EffectEvent, + IBeanFactory, + NextCaller, + StatedBeanApplication, + StatedBeanProvider, + useBean, +} from 'stated-bean'; import ReactDOM from 'react-dom'; import React from 'react'; @@ -29,22 +28,12 @@ const beanFactory: IBeanFactory = { }, }; -class LoggerInterceptor implements StatedInterceptor { - async stateInit(context: EffectContext, next: NextCaller) { - console.log('1. before init', context.toString()); - await next(); - console.log('1. after init', context.toString()); - } - - async stateChange(context: EffectContext, next: NextCaller) { - console.log('1. before change', context.toString()); - await next(); - console.log('1. after change', context.toString()); - } -} - app.setBeanFactory(beanFactory); -app.setInterceptors(new LoggerInterceptor()); +app.use(async (event: EffectEvent, next: NextCaller) => { + console.log('1. before change', event.type, event.name); + await next(); + console.log('1. after change', event.type, event.name); +}); const App = () => { const model = useBean(() => beanFactory.get(TodoModel)); diff --git a/example/src/components/Todo/index.tsx b/example/src/components/Todo/index.tsx index b42c03bc..90daedb2 100644 --- a/example/src/components/Todo/index.tsx +++ b/example/src/components/Todo/index.tsx @@ -1,7 +1,7 @@ import { TodoModel } from '../../models/TodoModel'; import { Todo } from '../../services/TodoService'; -import { useInject } from 'stated-bean'; +import { useInject, useObserveEffect } from 'stated-bean'; import React from 'react'; function TodoList(props: { items: Todo[] }) { @@ -16,10 +16,14 @@ function TodoList(props: { items: Todo[] }) { export const TodoApp = () => { const todo = useInject(TodoModel); + const { loading } = useObserveEffect(todo, 'fetchTodo'); + + console.log(loading); return (

TODO

+ {loading && 'loading'}
{ diff --git a/example/src/models/CounterModel.ts b/example/src/models/CounterModel.ts index c3df983a..30f3b93a 100644 --- a/example/src/models/CounterModel.ts +++ b/example/src/models/CounterModel.ts @@ -1,6 +1,6 @@ import { Injectable } from 'injection-js'; -import { StatedBean, Stated } from 'stated-bean'; +import { StatedBean, Stated, Effect } from 'stated-bean'; @StatedBean() @Injectable() @@ -12,9 +12,10 @@ export class CounterModel { this.count = count; } - increment = () => { + @Effect('11') + increment() { this.count++; - }; + } decrement = () => { this.count--; diff --git a/example/src/models/TodoModel.ts b/example/src/models/TodoModel.ts index 58595b8b..6d2ff32c 100644 --- a/example/src/models/TodoModel.ts +++ b/example/src/models/TodoModel.ts @@ -2,7 +2,7 @@ import { Injectable, Inject } from 'injection-js'; import { TodoService, Todo } from '../services/TodoService'; -import { StatedBean, Stated, PostProvided } from 'stated-bean'; +import { StatedBean, Stated, PostProvided, Effect } from 'stated-bean'; @StatedBean() @Injectable() @@ -16,6 +16,7 @@ export class TodoModel { constructor(@Inject(TodoService) private readonly todoService: TodoService) {} @PostProvided() + @Effect('fetchTodo') async fetchTodo() { this.todoList = await this.todoService.fetchTodoList(); } diff --git a/example/src/services/TodoService.ts b/example/src/services/TodoService.ts index fc151b3c..9cdf3fca 100644 --- a/example/src/services/TodoService.ts +++ b/example/src/services/TodoService.ts @@ -18,7 +18,7 @@ export class TodoService { state: 'todo', }, ]); - }, 1); + }, 1000); }); } } diff --git a/package.json b/package.json index c935e5ad..b3a5b4cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "stated-bean", - "version": "0.2.0", + "version": "0.3.0-alpha", "repository": "git@github.com:mjolnirjs/stated-bean.git", "license": "MIT", "main": "./lib/cjs/index.js", @@ -42,16 +42,13 @@ "@babel/core": "^7.5.5", "@commitlint/cli": "^8.1.0", "@commitlint/config-conventional": "^8.1.0", - "@types/enzyme": "^3.10.3", - "@types/enzyme-adapter-react-16": "^1.0.5", + "@testing-library/react-hooks": "^2.0.1", "@types/jest": "^24.0.18", "@types/node": "^12.7.3", "@types/react": "^16.9.2", "@types/react-dom": "^16.9.0", "@types/react-test-renderer": "^16.9.0", "cross-env": "^5.2.1", - "enzyme": "^3.10.0", - "enzyme-adapter-react-16": "^1.14.0", "eslint": "^6.3.0", "eslint-formatter-friendly": "^7.0.0", "husky": "^3.0.5", diff --git a/src/core/AutoBind.ts b/src/core/AutoBind.ts new file mode 100644 index 00000000..a01424e9 --- /dev/null +++ b/src/core/AutoBind.ts @@ -0,0 +1,86 @@ +export function boundMethod( + target: Function, + key: string | number | symbol, + descriptor: PropertyDescriptor, +) { + let fn = descriptor.value; + + if (typeof fn !== 'function') { + throw new TypeError( + `@boundMethod decorator can only be applied to methods not: ${typeof fn}`, + ); + } + + // In IE11 calling Object.defineProperty has a side-effect of evaluating the + // getter for the property which is being replaced. This causes infinite + // recursion and an "Out of stack space" error. + let definingProperty = false; + + return { + configurable: true, + get() { + // eslint-disable-next-line no-prototype-builtins + if ( + definingProperty || + this === target.prototype || + Object.hasOwnProperty.call(this, key) || + typeof fn !== 'function' + ) { + return fn; + } + + const boundFn = fn.bind(this); + definingProperty = true; + Object.defineProperty(this, key, { + configurable: true, + get() { + return boundFn; + }, + set(value) { + fn = value; + delete this[key]; + }, + }); + definingProperty = false; + return boundFn; + }, + set(value: unknown) { + fn = value; + }, + }; +} + +export function boundClass(target: Function) { + // (Using reflect to get all keys including symbols) + let keys: Array; + // Use Reflect if exists + if (typeof Reflect !== 'undefined' && typeof Reflect.ownKeys === 'function') { + keys = Reflect.ownKeys(target.prototype); + } else { + keys = Object.getOwnPropertyNames(target.prototype); + // Use symbols if support is provided + // eslint-disable-next-line @typescript-eslint/unbound-method + if (typeof Object.getOwnPropertySymbols === 'function') { + keys = keys.concat(Object.getOwnPropertySymbols(target.prototype)); + } + } + + keys.forEach(key => { + // Ignore special case target method + if (key === 'constructor') { + return; + } + + const descriptor = Object.getOwnPropertyDescriptor(target.prototype, key); + + // Only methods need binding + if (descriptor !== undefined && typeof descriptor.value === 'function') { + Object.defineProperty( + target.prototype, + key, + boundMethod(target, key, descriptor), + ); + } + }); + return target; +} diff --git a/src/core/EffectContext.ts b/src/core/EffectContext.ts deleted file mode 100644 index ed125c28..00000000 --- a/src/core/EffectContext.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { StatedBeanMeta, StatedFieldMeta } from '../types'; - -import { StatedBeanContainer } from './StatedBeanContainer'; - -export class EffectContext { - constructor( - // @internal - private readonly _oldValue: Value, - // @internal - private readonly _bean: Bean, - // @internal - private readonly _beanMeta: StatedBeanMeta, - // @internal - private readonly _fieldMeta: StatedFieldMeta, - // @internal - private readonly _container: StatedBeanContainer, - // @internal - private _value?: Value, - ) {} - - get bean(): Bean { - return this._bean; - } - - get beanMeta(): StatedBeanMeta { - return this._beanMeta; - } - - get fieldMeta(): StatedFieldMeta { - return this._fieldMeta; - } - - get oldValue(): Value { - return this._oldValue; - } - - get container(): StatedBeanContainer { - return this._container; - } - - setValue(value: Value) { - this._value = value; - } - - getValue() { - return this._value; - } - - toString() { - return `[${String(this.beanMeta.name)}] ${String(this.fieldMeta.name)} ${ - this.oldValue - } => ${this._value}`; - } -} diff --git a/src/core/EffectEvent.ts b/src/core/EffectEvent.ts new file mode 100644 index 00000000..9b894045 --- /dev/null +++ b/src/core/EffectEvent.ts @@ -0,0 +1,18 @@ +export enum EffectEventType { + StateChanged = 'StateChanged', + EffectAction = 'EffectAction', +} + +export class EffectEvent { + constructor( + readonly target: Bean, + readonly type: EffectEventType, + readonly name: string | symbol, + readonly value: Value, + ) { + this.target = target; + this.type = type; + this.name = name; + this.value = value; + } +} diff --git a/src/core/StatedBeanApplication.ts b/src/core/StatedBeanApplication.ts index 8e664321..7774dc10 100644 --- a/src/core/StatedBeanApplication.ts +++ b/src/core/StatedBeanApplication.ts @@ -1,8 +1,7 @@ -import { StatedInterceptor } from '../interceptor/StatedInterceptor'; -import { ClassType } from '../types'; +import { MiddlewareMethod } from '../middleware'; import { IBeanFactory, DefaultBeanFactory } from './StatedBeanFactory'; -import { EffectContext } from './EffectContext'; +import { EffectEvent } from './EffectEvent'; /** * @@ -12,7 +11,7 @@ import { EffectContext } from './EffectContext'; export class StatedBeanApplication { private _beanFactory: IBeanFactory = new DefaultBeanFactory(); - private _interceptors: StatedInterceptor[] = []; + private readonly _middleware: MiddlewareMethod[] = []; getBeanFactory(): IBeanFactory { return this._beanFactory; @@ -23,47 +22,12 @@ export class StatedBeanApplication { return this; } - getInterceptors() { - return this._interceptors; - } - - setInterceptors(...interceptors: StatedInterceptor[]) { - this._interceptors = [...interceptors]; - return this; - } - - addInterceptors( - ...interceptors: Array> - ) { - interceptors.forEach(interceptor => { - if (typeof interceptor === 'function') { - const interceptorBean = this.getBeanFactory().get(interceptor); - if (interceptorBean) { - this._interceptors.push(interceptorBean); - } else { - throw new Error( - `get interceptor[${interceptor.name}] from bean factory fail`, - ); - } - } else { - this._interceptors.push(interceptor); - } - }); + use(middleware: MiddlewareMethod) { + this._middleware.push(middleware); return this; } - interceptStateInit(effect: EffectContext) { - return this._invokeInterceptors('stateInit', effect); - } - - interceptStateChange(effect: EffectContext) { - return this._invokeInterceptors('stateChange', effect); - } - - private _invokeInterceptors( - method: keyof StatedInterceptor, - effect: EffectContext, - ) { + invokeMiddleware(effect: EffectEvent) { let index = -1; const dispatch = (i: number): Promise => { if (i <= index) { @@ -71,21 +35,17 @@ export class StatedBeanApplication { } index = i; - let interceptor: StatedInterceptor | undefined; - if (i < this._interceptors.length) { - interceptor = this._interceptors[i]; + let middleware: MiddlewareMethod | undefined; + if (i < this._middleware.length) { + middleware = this._middleware[i]; } - const fn = interceptor && interceptor[method]; - - if (!fn) { + if (!middleware) { return Promise.resolve(); } try { - return Promise.resolve( - fn.apply(interceptor, [effect, dispatch.bind(this, i + 1)]), - ); + return Promise.resolve(middleware(effect, dispatch.bind(this, i + 1))); } catch (e) { return Promise.reject(e); } diff --git a/src/core/StatedBeanContainer.ts b/src/core/StatedBeanContainer.ts index cd0c0b43..b696f22c 100644 --- a/src/core/StatedBeanContainer.ts +++ b/src/core/StatedBeanContainer.ts @@ -3,13 +3,14 @@ import { getMetadataStorage } from '../metadata'; import { BeanRegisterOption, ClassType, + StateChanged, StatedBeanMeta, StatedBeanType, StatedFieldMeta, } from '../types'; import { isFunction } from '../utils'; -import { EffectContext } from './EffectContext'; +import { EffectEvent, EffectEventType } from './EffectEvent'; import { NoSuchBeanDefinitionError } from './NoSuchBeanDefinitionError'; import { StatedBeanApplication } from './StatedBeanApplication'; import { StatedBeanRegistry } from './StatedBeanRegistry'; @@ -174,12 +175,16 @@ export class StatedBeanContainer extends Event { if (fieldMeta === undefined) { return; } - const effect = self._createEffectContext( - bean[field], + const effect = new EffectEvent>( bean, - beanMeta, - fieldMeta, - bean[field], + EffectEventType.StateChanged, + field, + { + newValue: bean[field], + oldValue: bean[field], + fieldMeta, + beanMeta, + }, ); self.emit(bean, effect); @@ -188,47 +193,37 @@ export class StatedBeanContainer extends Event { } // @internal - private async _observeBeanField( + private _observeBeanField( // eslint-disable-next-line @typescript-eslint/no-explicit-any - bean: any, + bean: T, fieldMeta: StatedFieldMeta, beanMeta: StatedBeanMeta, ) { - const proxyField = Symbol(fieldMeta.name.toString() + '_v'); - - const initEffect = this._createEffectContext( - bean[proxyField], - bean, - beanMeta, - fieldMeta, - bean[fieldMeta.name], - ); - await this.application.interceptStateInit(initEffect); + const proxyField = Symbol(fieldMeta.name.toString() + '_v') as keyof T; Object.defineProperty(bean, proxyField, { writable: true, - value: initEffect.getValue(), + value: bean[fieldMeta.name as keyof T], }); const self = this; Object.defineProperty(bean, fieldMeta.name.toString(), { set(value) { - const effect = self._createEffectContext( - bean[proxyField], + const effect = new EffectEvent>( bean, - beanMeta, - fieldMeta, - value, + EffectEventType.StateChanged, + fieldMeta.name, + { + newValue: value, + oldValue: bean[proxyField], + fieldMeta, + beanMeta, + }, ); bean[proxyField] = value; self.emit(bean, effect); - self.application.interceptStateChange(effect).then(() => { - if (effect.getValue() !== value) { - bean[proxyField] = effect.getValue(); - self.emit(bean, effect); - } - }); + self.application.invokeMiddleware(effect); }, get() { return bean[proxyField]; @@ -236,17 +231,6 @@ export class StatedBeanContainer extends Event { }); } - // @internal - private _createEffectContext( - oldValue: Value, - bean: Bean, - beanMeta: StatedBeanMeta, - fieldMeta: StatedFieldMeta, - value?: Value, - ): EffectContext { - return new EffectContext(oldValue, bean, beanMeta, fieldMeta, this, value); - } - get parent() { return this._parent; } diff --git a/src/core/index.ts b/src/core/index.ts index 5c3633f0..c649e914 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -1,5 +1,6 @@ -export * from './EffectContext'; export * from './StatedBeanApplication'; export * from './StatedBeanContainer'; export * from './StatedBeanFactory'; export * from './Symbols'; +export * from './AutoBind'; +export * from './EffectEvent'; diff --git a/src/decorator/Effect.ts b/src/decorator/Effect.ts new file mode 100644 index 00000000..efed2b37 --- /dev/null +++ b/src/decorator/Effect.ts @@ -0,0 +1,60 @@ +import { StatedBeanType, EffectAction } from '../types'; +import { StatedBeanSymbol, EffectEvent, EffectEventType } from '../core'; +import { isPromise } from '../utils'; + +/** + * + * @export + * @returns {MethodDecorator} + */ +export function Effect(name?: string | symbol): MethodDecorator { + return ( + prototype, + propertyKey, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + descriptor?: TypedPropertyDescriptor, + ) => { + if (descriptor === undefined) { + descriptor = Object.getOwnPropertyDescriptor(prototype, propertyKey)!; + } + const effectName = name || propertyKey; + const originalMethod = descriptor.value; + descriptor.value = function( + this: StatedBeanType, + ...args: unknown[] + ) { + const container = this[StatedBeanSymbol].container; + + const emitEffectAction = (action: EffectAction) => { + container.emit( + this, + new EffectEvent( + this, + EffectEventType.EffectAction, + effectName, + action, + ), + ); + }; + + emitEffectAction({ loading: true, error: null }); + const result = originalMethod.apply(this, args); + + if (isPromise(result)) { + result + .then((data: unknown) => { + emitEffectAction({ loading: false, error: null }); + return data; + }) + .catch((e: unknown) => { + emitEffectAction({ loading: false, error: e }); + return e; + }); + } else { + emitEffectAction({ loading: false, error: null }); + } + return result; + }; + return descriptor; + }; +} diff --git a/src/decorator/StatedBean.ts b/src/decorator/StatedBean.ts index 62434da4..cb0eb401 100644 --- a/src/decorator/StatedBean.ts +++ b/src/decorator/StatedBean.ts @@ -1,5 +1,5 @@ import { getMetadataStorage } from '../metadata'; -import { StatedBeanSymbol } from '../core'; +import { StatedBeanSymbol, boundClass } from '../core'; /** * Indicates that an annotated class is a `StatedBean`. @@ -11,6 +11,7 @@ import { StatedBeanSymbol } from '../core'; export function StatedBean(): ClassDecorator; export function StatedBean(name?: string | symbol): ClassDecorator { return (target: Function) => { + boundClass(target); Object.defineProperty(target, StatedBeanSymbol, { writable: false, value: true, diff --git a/src/decorator/index.ts b/src/decorator/index.ts index 8a0eea26..404a18a6 100644 --- a/src/decorator/index.ts +++ b/src/decorator/index.ts @@ -1,3 +1,4 @@ export * from './Stated'; export * from './StatedBean'; export * from './PostProvided'; +export * from './Effect'; diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 3b86087f..54f4bef3 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -1,3 +1,4 @@ export * from './useContainer'; export * from './useInject'; export * from './useBean'; +export * from './useObserveEffect'; diff --git a/src/hooks/useBean.ts b/src/hooks/useBean.ts index 0338b93e..43a8498d 100644 --- a/src/hooks/useBean.ts +++ b/src/hooks/useBean.ts @@ -1,5 +1,5 @@ import { getStatedBeanContext } from '../context'; -import { EffectContext, StatedBeanContainer } from '../core'; +import { EffectEvent, StatedBeanContainer } from '../core'; import { ClassType, StatedBeanType } from '../types'; import { isFunction, isStatedBean } from '../utils'; @@ -26,7 +26,7 @@ export function useBean( const context = useContext(StateBeanContext); const [, setVersion] = useState(0); - const beanChangeListener = useCallback((_effect: EffectContext) => { + const beanChangeListener = useCallback((_effect: EffectEvent) => { setVersion(prev => prev + 1); }, []); diff --git a/src/hooks/useInject.ts b/src/hooks/useInject.ts index 8242abb1..352c2998 100644 --- a/src/hooks/useInject.ts +++ b/src/hooks/useInject.ts @@ -1,6 +1,6 @@ import { getStatedBeanContext } from '../context'; -import { EffectContext } from '../core'; -import { ClassType, StatedBeanType } from '../types'; +import { EffectEvent, EffectEventType } from '../core'; +import { ClassType, StateChanged, StatedBeanType } from '../types'; import { useCallback, useContext, useEffect, useState } from 'react'; @@ -27,14 +27,17 @@ export function useInject( const [, setVersion] = useState(0); const beanChangeListener = useCallback( - (effect: EffectContext) => { - const field = effect.fieldMeta.name as keyof T; - if ( - option.fields == null || - option.fields.length === 0 || - option.fields.includes(field) - ) { - setVersion(prev => prev + 1); + (event: EffectEvent>) => { + if (event.type === EffectEventType.StateChanged) { + const changedState = event.value; + const field = changedState.fieldMeta.name as keyof T; + if ( + option.fields == null || + option.fields.length === 0 || + option.fields.includes(field) + ) { + setVersion(prev => prev + 1); + } } }, [option.fields], diff --git a/src/hooks/useObserveEffect.ts b/src/hooks/useObserveEffect.ts new file mode 100644 index 00000000..fa3e880d --- /dev/null +++ b/src/hooks/useObserveEffect.ts @@ -0,0 +1,42 @@ +import { EffectAction, StatedBeanType } from '../types'; +import { StatedBeanSymbol, EffectEvent, EffectEventType } from '../core'; + +import { useState, useEffect, useCallback } from 'react'; + +export function useObserveEffect>( + bean: T, + effect: string | symbol, +): EffectAction { + const [effectState, setEffectState] = useState(() => { + return { + loading: false, + error: null, + }; + }); + + const listener = useCallback( + (event: EffectEvent) => { + if ( + event.type === EffectEventType.EffectAction && + event.name === effect + ) { + setEffectState(event.value); + } + }, + [effect], + ); + const [container] = useState(() => { + const statedBean = bean as StatedBeanType; + const container = statedBean[StatedBeanSymbol].container; + + container.on(bean, listener); + return container; + }); + + useEffect(() => { + return () => { + container.off(bean, listener); + }; + }, [container, bean, listener]); + return effectState; +} diff --git a/src/index.ts b/src/index.ts index 9dfbda33..98cc17f8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,4 +3,4 @@ export * from './decorator'; export * from './hooks'; export * from './context'; export * from './types'; -export * from './interceptor'; +export * from './middleware'; diff --git a/src/interceptor/StatedInterceptor.ts b/src/interceptor/StatedInterceptor.ts deleted file mode 100644 index c4e2761f..00000000 --- a/src/interceptor/StatedInterceptor.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { EffectContext } from '../core'; - -export type NextCaller = () => Promise; - -export type InterceptMethod = ( - context: EffectContext, - next: NextCaller, -) => Promise; - -export interface StatedInterceptor { - stateInit: InterceptMethod; - stateChange: InterceptMethod; -} diff --git a/src/interceptor/index.ts b/src/interceptor/index.ts deleted file mode 100644 index 67fc418a..00000000 --- a/src/interceptor/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './StatedInterceptor'; diff --git a/src/middleware/EffectMiddleware.ts b/src/middleware/EffectMiddleware.ts new file mode 100644 index 00000000..a02f7a57 --- /dev/null +++ b/src/middleware/EffectMiddleware.ts @@ -0,0 +1,8 @@ +import { EffectEvent } from '../core'; + +export type NextCaller = () => Promise; + +export type MiddlewareMethod = ( + context: EffectEvent, + next: NextCaller, +) => Promise; diff --git a/src/middleware/index.ts b/src/middleware/index.ts new file mode 100644 index 00000000..2ba5a55d --- /dev/null +++ b/src/middleware/index.ts @@ -0,0 +1 @@ +export * from './EffectMiddleware'; diff --git a/src/types/EffectEventValue.ts b/src/types/EffectEventValue.ts new file mode 100644 index 00000000..092be629 --- /dev/null +++ b/src/types/EffectEventValue.ts @@ -0,0 +1,13 @@ +import { StatedBeanMeta, StatedFieldMeta } from './StatedMetadata'; + +export interface EffectAction { + loading: boolean; + error: unknown; +} + +export interface StateChanged { + newValue: T; + oldValue: T; + fieldMeta: StatedFieldMeta; + beanMeta: StatedBeanMeta; +} diff --git a/src/types/index.ts b/src/types/index.ts index 4d4d4cd0..bb4e44d7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -2,3 +2,4 @@ export * from './ClassType'; export * from './StatedMetadata'; export * from './Provider'; export * from './StatedBeanType'; +export * from './EffectEventValue'; diff --git a/src/utils/index.ts b/src/utils/index.ts index 9ca48f66..a0bcb85b 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,3 +4,7 @@ export const isFunction = (func: unknown) => typeof func === 'function'; export const isStatedBean = (obj: unknown) => Object.hasOwnProperty.call(obj, StatedBeanSymbol); + +export function isPromise(value: unknown): value is Promise { + return !!value && typeof (value as { then: unknown }).then === 'function'; +} diff --git a/test/application.test.ts b/test/application.test.ts index 54aa39b1..1a186453 100644 --- a/test/application.test.ts +++ b/test/application.test.ts @@ -2,14 +2,13 @@ import { StatedBeanApplication, IBeanFactory, ClassType, - StatedInterceptor, - EffectContext, NextCaller, StatedBean, Stated, StatedBeanContainer, + EffectEvent, + StateChanged, } from '../src'; -import { getMetadataStorage } from '../src/metadata'; @StatedBean() class SampleStatedBean { @@ -41,38 +40,17 @@ describe('StatedBeanApplication', () => { }); it('application interceptor test', async () => { - class CustomInterceptor implements StatedInterceptor { - async stateInit(context: EffectContext, next: NextCaller) { - expect( - context.bean === context.container.getBean(SampleStatedBean), - ).toEqual(true); - expect( - context.beanMeta === - getMetadataStorage().getBeanMeta(SampleStatedBean), - ).toEqual(true); - expect( - context.beanMeta.statedFields!.includes(context.fieldMeta), - ).toEqual(true); - await next(); - } - - async stateChange(_context: EffectContext, next: NextCaller) { - await next(); - } - } + const CustomMiddleware = async ( + event: EffectEvent>, + next: NextCaller, + ) => { + expect(event.value.newValue).toEqual(1); + await next(); + }; const application = new StatedBeanApplication(); - application.setInterceptors(new CustomInterceptor()); - application.addInterceptors(CustomInterceptor, new CustomInterceptor()); - - expect(application.getInterceptors().length).toEqual(3); - expect( - application.getInterceptors()[0] instanceof CustomInterceptor, - ).toEqual(true); - expect( - application.getInterceptors()[1] instanceof CustomInterceptor, - ).toEqual(true); + application.use(CustomMiddleware); const container = new StatedBeanContainer(undefined, application); diff --git a/test/effect-action.test.ts b/test/effect-action.test.ts new file mode 100644 index 00000000..ef424660 --- /dev/null +++ b/test/effect-action.test.ts @@ -0,0 +1,36 @@ +import { act, renderHook } from '@testing-library/react-hooks'; + +import { Effect, Stated, StatedBean, useBean, useObserveEffect } from '../src'; + +import { delay } from './utils'; + +@StatedBean() +class PostProvidedSample { + @Stated() + test = 0; + + @Effect('add') + async add() { + await delay(100); + this.test += 1; + } +} + +describe('effect action', () => { + it('effect action change', async () => { + const { result, unmount } = renderHook(() => { + const bean = useBean(() => new PostProvidedSample()); + const action = useObserveEffect(bean, 'add'); + return { bean, action }; + }); + expect(result.current.action.loading).toBe(false); + const addPromise = act(() => { + return result.current.bean.add(); + }); + expect(result.current.action.loading).toBe(true); + await addPromise; + expect(result.current.action.loading).toBe(false); + + unmount(); + }); +}); diff --git a/test/post-provided.test.ts b/test/post-provided.test.ts index b958e784..be46c305 100644 --- a/test/post-provided.test.ts +++ b/test/post-provided.test.ts @@ -1,4 +1,8 @@ -import { StatedBean, Stated, PostProvided, StatedBeanContainer } from '../src'; +import { renderHook } from '@testing-library/react-hooks'; + +import { PostProvided, Stated, StatedBean, useBean } from '../src'; + +import { delay } from './utils'; @StatedBean() class PostProvidedSample { @@ -6,20 +10,18 @@ class PostProvidedSample { test = 0; @PostProvided() - postMethod() { + async postMethod() { + await delay(100); this.test = 1; } } describe('PostProvided', () => { it('PostProvided method invoke', async () => { - const container = new StatedBeanContainer(); - await container.register(PostProvidedSample); - - const bean = container.getBean(PostProvidedSample); - - if (bean) { - expect(bean.test).toEqual(1); - } + const { result, waitForNextUpdate } = renderHook(() => { + return useBean(() => new PostProvidedSample()); + }); + await waitForNextUpdate(); + expect(result.current.test).toEqual(1); }); }); diff --git a/test/stated-change.test.tsx b/test/stated-change.test.tsx index b342077d..4145b273 100644 --- a/test/stated-change.test.tsx +++ b/test/stated-change.test.tsx @@ -1,3 +1,5 @@ +import { act, renderHook } from '@testing-library/react-hooks'; + import { Stated, StatedBean, @@ -7,14 +9,10 @@ import { useInject, } from '../src'; -import Enzyme from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; import React from 'react'; import ReactDOM from 'react-dom'; import renderer from 'react-test-renderer'; -Enzyme.configure({ adapter: new Adapter() }); - @StatedBean() class SampleStatedBean { @Stated() @@ -29,13 +27,9 @@ class SampleStatedBean { } describe('stated value changed test', () => { - const TestStatedBean = SampleStatedBean; - - // beforeAll(() => getMetadataStorage().clear()); - it('StatedBeanProvider', () => { const Sample = () => { - const bean = useInject(TestStatedBean); + const bean = useInject(SampleStatedBean); expect(bean).not.toBeNull(); expect(bean.statedField).toEqual(0); @@ -49,9 +43,9 @@ describe('stated value changed test', () => { }; const App = () => ( - + - + @@ -63,29 +57,22 @@ describe('stated value changed test', () => { }); it('useInject and change the stated field', () => { - const Sample = () => { - const bean = useInject(TestStatedBean); - return ( - <> - field={bean.statedField} - - - ); - }; - - const App = () => ( - - + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} ); + const { result } = renderHook( + () => { + return useInject(SampleStatedBean); + }, + { wrapper }, + ); - const app = Enzyme.mount(); - - expect(app.find('.field').text()).toBe('field=0'); - - app.find('button').simulate('click'); - - setTimeout(() => expect(app.find('.field').text()).toBe('field=1'), 100); + act(() => { + result.current.addStatedField(); + }); + expect(result.current.statedField).toEqual(1); }); it('StatedBeanConsumer', () => { @@ -100,7 +87,7 @@ describe('stated value changed test', () => { ); const App = () => ( - + ); diff --git a/test/use-bean.test.tsx b/test/use-bean.test.tsx index 0223fa0b..92aba512 100644 --- a/test/use-bean.test.tsx +++ b/test/use-bean.test.tsx @@ -1,37 +1,18 @@ -import { Stated, StatedBean, useBean } from '../src'; - -import Enzyme from 'enzyme'; -import Adapter from 'enzyme-adapter-react-16'; -import React from 'react'; - -Enzyme.configure({ adapter: new Adapter() }); +import { renderHook } from '@testing-library/react-hooks'; -@StatedBean() -class StatedBeanScopeSample { - @Stated() - test = 0; -} +import { Stated, StatedBean, useBean } from '../src'; @StatedBean() -class StatedBeanScopeSample2 { +class StatedBeanSample { @Stated() test = 0; } describe('useBean test', () => { it('bean create', () => { - const Sample = () => { - const bean = useBean(StatedBeanScopeSample); - expect(bean).not.toBeNull(); - - const bean2 = useBean(() => new StatedBeanScopeSample2()); - expect(bean2).not.toBeNull(); - - return <>{bean.test}; - }; - - const App = () => ; - - Enzyme.mount(); + const { result } = renderHook(() => { + return useBean(() => new StatedBeanSample()); + }); + expect(result.current).not.toBeNull(); }); }); diff --git a/test/use-inject.test.tsx b/test/use-inject.test.tsx new file mode 100644 index 00000000..afdb11cc --- /dev/null +++ b/test/use-inject.test.tsx @@ -0,0 +1,53 @@ +import { renderHook } from '@testing-library/react-hooks'; + +import { useInject, StatedBean, StatedBeanProvider } from '../src'; + +import React from 'react'; + +@StatedBean() +class SampleStatedBean { + name = ''; +} + +describe('useInject test', () => { + it('useInject get bean from context', () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + + {children} + + ); + const { result, unmount } = renderHook( + () => { + return useInject(SampleStatedBean); + }, + { wrapper }, + ); + + expect(result.current).not.toBeNull(); + + unmount(); + }); + + it('useInject get fail when without context', () => { + renderHook(() => { + expect(() => { + useInject(SampleStatedBean); + }).toThrow(); + }); + }); + + it('useInject get fail when bean not provided', () => { + const wrapper = ({ children }: { children: React.ReactNode }) => ( + {children} + ); + + renderHook( + () => { + expect(() => { + useInject(SampleStatedBean); + }).toThrow(); + }, + { wrapper }, + ); + }); +}); diff --git a/test/utils.ts b/test/utils.ts new file mode 100644 index 00000000..698d7c6b --- /dev/null +++ b/test/utils.ts @@ -0,0 +1,7 @@ +export const delay = (time: number) => { + return new Promise(resolve => { + setTimeout(() => { + resolve(); + }, time); + }); +}; diff --git a/yarn.lock b/yarn.lock index f631b833..9beda4d3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1056,7 +1056,7 @@ dependencies: regenerator-runtime "^0.12.0" -"@babel/runtime@^7.4.2", "@babel/runtime@^7.5.5": +"@babel/runtime@^7.4.2", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132" integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ== @@ -1717,6 +1717,15 @@ "@styled-system/core" "^5.1.1" "@styled-system/css" "^5.0.23" +"@testing-library/react-hooks@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-2.0.1.tgz#1c3ec40882d0830df3078ddae0056fdf7366c81d" + integrity sha512-MLTvWX7/csq/uQzP4WJntGz0QJDq6H4EzjV0VTL5YJE7KBZbaQ9DGT0IbtjuB33L4R4YKZ55rGZQ5eL+WiZtQA== + dependencies: + "@babel/runtime" "^7.5.4" + "@types/react" ">=16.9.0" + "@types/react-test-renderer" ">=16.9.0" + "@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" @@ -1750,28 +1759,6 @@ dependencies: "@babel/types" "^7.3.0" -"@types/cheerio@*": - version "0.22.13" - resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.13.tgz#5eecda091a24514185dcba99eda77e62bf6523e6" - integrity sha512-OZd7dCUOUkiTorf97vJKwZnSja/DmHfuBAroe1kREZZTCf/tlFecwHhsOos3uVHxeKGZDwzolIrCUApClkdLuA== - dependencies: - "@types/node" "*" - -"@types/enzyme-adapter-react-16@^1.0.5": - version "1.0.5" - resolved "https://registry.yarnpkg.com/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.5.tgz#1bf30a166f49be69eeda4b81e3f24113c8b4e9d5" - integrity sha512-K7HLFTkBDN5RyRmU90JuYt8OWEY2iKUn43SDWEoBOXd/PowUWjLZ3Q6qMBiQuZeFYK/TOstaZxsnI0fXoAfLpg== - dependencies: - "@types/enzyme" "*" - -"@types/enzyme@*", "@types/enzyme@^3.10.3": - version "3.10.3" - resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.3.tgz#02b6c5ac7d0472005944a652e79045e2f6c66804" - integrity sha512-f/Kcb84sZOSZiBPCkr4He9/cpuSLcKRyQaEE20Q30Prx0Dn6wcyMAWI0yofL6yvd9Ht9G7EVkQeRqK0n5w8ILw== - dependencies: - "@types/cheerio" "*" - "@types/react" "*" - "@types/eslint-visitor-keys@^1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" @@ -1870,14 +1857,14 @@ dependencies: "@types/react" "*" -"@types/react-test-renderer@^16.9.0": +"@types/react-test-renderer@>=16.9.0", "@types/react-test-renderer@^16.9.0": version "16.9.0" resolved "https://registry.yarnpkg.com/@types/react-test-renderer/-/react-test-renderer-16.9.0.tgz#d60f530ecf4c906721511603cca711b4fa830d41" integrity sha512-bN5EyjtuTY35xX7N5j0KP1vg5MpUXHpFTX6tGsqkNOthjNvet4VQOYRxFh+NT5cDSJrATmAFK9NLeYZ4mp/o0Q== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@^16.9.2": +"@types/react@*", "@types/react@>=16.9.0", "@types/react@^16.9.2": version "16.9.2" resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.2.tgz#6d1765431a1ad1877979013906731aae373de268" integrity sha512-jYP2LWwlh+FTqGd9v7ynUKZzjj98T8x7Yclz479QdRhHfuW9yQ+0jjnD31eXSXutmBpppj5PYNLYLRfnZJvcfg== @@ -2111,22 +2098,6 @@ aggregate-error@^3.0.0: clean-stack "^2.0.0" indent-string "^3.2.0" -airbnb-prop-types@^2.13.2: - version "2.15.0" - resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.15.0.tgz#5287820043af1eb469f5b0af0d6f70da6c52aaef" - integrity sha512-jUh2/hfKsRjNFC4XONQrxo/n/3GG4Tn6Hl0WlFQN5PY9OMC9loSCoAYKnZsWaP8wEfd5xcrPloK0Zg6iS1xwVA== - dependencies: - array.prototype.find "^2.1.0" - function.prototype.name "^1.1.1" - has "^1.0.3" - is-regex "^1.0.4" - object-is "^1.0.1" - object.assign "^4.1.0" - object.entries "^1.1.0" - prop-types "^15.7.2" - prop-types-exact "^1.2.0" - react-is "^16.9.0" - ajv@6.5.3: version "6.5.3" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.3.tgz#71a569d189ecf4f4f321224fecb166f071dd90f9" @@ -2266,11 +2237,6 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= -array-filter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" - integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= - array-find-index@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" @@ -2299,23 +2265,6 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.find@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.0.tgz#630f2eaf70a39e608ac3573e45cf8ccd0ede9ad7" - integrity sha512-Wn41+K1yuO5p7wRZDl7890c3xvv5UBrfVXTVIe28rSQb6LS0fZMDrQB6PAcxQFRFy6vJTLDc3A2+3CjQdzVKRg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.13.0" - -array.prototype.flat@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4" - integrity sha512-rVqIs330nLJvfC7JqYvEWwqVr5QjYF1ib02i3YJtR/fICO6527Tjpc/e4Mvmxh3GIePPreRXMdaGyC99YphWEw== - dependencies: - define-properties "^1.1.2" - es-abstract "^1.10.0" - function-bind "^1.1.1" - arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -2937,18 +2886,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -cheerio@^1.0.0-rc.2: - version "1.0.0-rc.3" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" - integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.1" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash "^4.15.0" - parse5 "^3.0.1" - chokidar@^2.0.3: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -3409,16 +3346,6 @@ css-select@^2.0.0: domutils "^1.7.0" nth-check "^1.0.2" -css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - css-selector-tokenizer@^0.7.0: version "0.7.1" resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.1.tgz#a177271a8bca5019172f4f891fc6eed9cbf68d5d" @@ -3449,7 +3376,7 @@ css-unit-converter@^1.1.1: resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" integrity sha1-2bkoGtz9jO2TW9urqDeGiX9k6ZY= -css-what@2.1, css-what@^2.1.2: +css-what@^2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== @@ -3782,11 +3709,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -discontinuous-range@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" - integrity sha1-44Mx8IRLukm5qctxx3FYWqsbxlo= - doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -3817,20 +3739,12 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== - dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" - domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: +domelementtype@1, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== @@ -3854,14 +3768,6 @@ domhandler@^2.3.0: dependencies: domelementtype "1" -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= - dependencies: - dom-serializer "0" - domelementtype "1" - domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" @@ -3964,7 +3870,7 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -entities@^1.1.1, entities@~1.1.1: +entities@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== @@ -3974,59 +3880,6 @@ entities@^2.0.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== -enzyme-adapter-react-16@^1.14.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.14.0.tgz#204722b769172bcf096cb250d33e6795c1f1858f" - integrity sha512-7PcOF7pb4hJUvjY7oAuPGpq3BmlCig3kxXGi2kFx0YzJHppqX1K8IIV9skT1IirxXlu8W7bneKi+oQ10QRnhcA== - dependencies: - enzyme-adapter-utils "^1.12.0" - has "^1.0.3" - object.assign "^4.1.0" - object.values "^1.1.0" - prop-types "^15.7.2" - react-is "^16.8.6" - react-test-renderer "^16.0.0-0" - semver "^5.7.0" - -enzyme-adapter-utils@^1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.12.0.tgz#96e3730d76b872f593e54ce1c51fa3a451422d93" - integrity sha512-wkZvE0VxcFx/8ZsBw0iAbk3gR1d9hK447ebnSYBf95+r32ezBq+XDSAvRErkc4LZosgH8J7et7H7/7CtUuQfBA== - dependencies: - airbnb-prop-types "^2.13.2" - function.prototype.name "^1.1.0" - object.assign "^4.1.0" - object.fromentries "^2.0.0" - prop-types "^15.7.2" - semver "^5.6.0" - -enzyme@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.10.0.tgz#7218e347c4a7746e133f8e964aada4a3523452f6" - integrity sha512-p2yy9Y7t/PFbPoTvrWde7JIYB2ZyGC+NgTNbVEGvZ5/EyoYSr9aG/2rSbVvyNvMHEhw9/dmGUJHWtfQIEiX9pg== - dependencies: - array.prototype.flat "^1.2.1" - cheerio "^1.0.0-rc.2" - function.prototype.name "^1.1.0" - has "^1.0.3" - html-element-map "^1.0.0" - is-boolean-object "^1.0.0" - is-callable "^1.1.4" - is-number-object "^1.0.3" - is-regex "^1.0.4" - is-string "^1.0.4" - is-subset "^0.1.1" - lodash.escape "^4.0.1" - lodash.isequal "^4.5.0" - object-inspect "^1.6.0" - object-is "^1.0.1" - object.assign "^4.1.0" - object.entries "^1.0.4" - object.values "^1.0.4" - raf "^3.4.0" - rst-selector-parser "^2.2.3" - string.prototype.trim "^1.1.2" - error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -4034,7 +3887,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.10.0, es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.13.0, es-abstract@^1.4.3, es-abstract@^1.5.1, es-abstract@^1.7.0: +es-abstract@^1.11.0, es-abstract@^1.12.0, es-abstract@^1.4.3, es-abstract@^1.5.1, es-abstract@^1.7.0: version "1.13.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== @@ -4789,26 +4642,11 @@ function-bind@^1.0.2, function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.0, function.prototype.name@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.1.tgz#6d252350803085abc2ad423d4fe3be2f9cbda392" - integrity sha512-e1NzkiJuw6xqVH7YSdiW/qDHebcmMhPNe6w+4ZYYEg0VA+LaLzx37RimbPLuonHhYGFGPx1ME2nSi74JiaCr/Q== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - functions-have-names "^1.1.1" - is-callable "^1.1.4" - functional-red-black-tree@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -functions-have-names@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.1.1.tgz#79d35927f07b8e7103d819fed475b64ccf7225ea" - integrity sha512-U0kNHUoxwPNPWOJaMG7Z00d4a/qZVrFtzWJRaK8V9goaVOCXBSQSJpt3MYGNtkScKEBKovxLjnNdC9MlXwo5Pw== - gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -5105,13 +4943,6 @@ html-comment-regex@^1.1.0: resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== -html-element-map@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.1.0.tgz#e5aab9a834caf883b421f8bd9eaedcaac887d63c" - integrity sha512-iqiG3dTZmy+uUaTmHarTL+3/A2VW9ox/9uasKEZC+R/wAtUrTcRlXPSaPqsnWPfIu8wqn09jQNwMRqzL54jSYA== - dependencies: - array-filter "^1.0.0" - html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -5143,7 +4974,7 @@ htmlnano@^0.2.2: terser "^4.1.2" uncss "^0.17.0" -htmlparser2@^3.9.1, htmlparser2@^3.9.2: +htmlparser2@^3.9.2: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -5388,11 +5219,6 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-boolean-object@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" - integrity sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M= - is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -5534,11 +5360,6 @@ is-html@^1.1.0: dependencies: html-tags "^1.0.0" -is-number-object@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.3.tgz#f265ab89a9f445034ef6aff15a8f00b00f551799" - integrity sha1-8mWrian0RQNO9q/xWo8AsA9VF5k= - is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -5622,16 +5443,6 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== -is-string@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.4.tgz#cc3a9b69857d621e963725a24caeec873b826e64" - integrity sha1-zDqbaYV9Yh6WNyWiTK7shzuCbmQ= - -is-subset@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" - integrity sha1-ilkRfZMt4d4A8kX83TnOQ/HpOaY= - is-svg@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" @@ -6453,21 +6264,6 @@ lodash.clone@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" integrity sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y= -lodash.escape@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98" - integrity sha1-yQRGkMIeBClL6qUXcS/e0fqI3pg= - -lodash.flattendeep@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" - integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= - -lodash.isequal@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" - integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= - lodash.kebabcase@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" @@ -6513,7 +6309,7 @@ lodash@4.17.14: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.14.tgz#9ce487ae66c96254fe20b599f21b6816028078ba" integrity sha512-mmKYbW3GLuJeX+iGP+Y7Gp1AiGHGbXHCOh/jZmrawMmsE7MS4znI3RL2FsjbqOyMayHInjOeykW7PEajUk1/xw== -lodash@^4.15.0, 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.2.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.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -6862,11 +6658,6 @@ mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "0.0.8" -moo@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" - integrity sha512-gFD2xGCl8YFgGHsqJ9NKRVdwlioeW3mI1iqfLNYQOv0+6JRwG58Zk9DIGQgyIaffSYaO1xsKnMaYzzNr1KyIAw== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -6914,17 +6705,6 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= -nearley@^2.7.10: - version "2.19.0" - resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.19.0.tgz#37717781d0fd0f2bfc95e233ebd75678ca4bda46" - integrity sha512-2v52FTw7RPqieZr3Gth1luAXZR7Je6q3KaDHY5bjl/paDUdMu35fZ8ICNgiYJRr3tf3NMvIQQR1r27AvEr9CRA== - dependencies: - commander "^2.19.0" - moo "^0.4.3" - railroad-diagrams "^1.0.0" - randexp "0.4.6" - semver "^5.4.1" - needle@^2.2.1: version "2.4.0" resolved "https://registry.yarnpkg.com/needle/-/needle-2.4.0.tgz#6833e74975c444642590e15a750288c5f939b57c" @@ -7124,7 +6904,7 @@ npmlog@^4.0.2: gauge "~2.7.3" set-blocking "~2.0.0" -nth-check@^1.0.2, nth-check@~1.0.1: +nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== @@ -7160,21 +6940,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-inspect@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.6.0.tgz#c70b6cbf72f274aab4c34c0c82f5167bf82cf15b" - integrity sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ== - object-inspect@~1.4.0: version "1.4.1" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.4.1.tgz#37ffb10e71adaf3748d05f713b4c9452f402cbc4" integrity sha512-wqdhLpfCUbEsoEwl3FXwGyv8ief1k/1aUdIPCqVnupM6e8l63BEJdiF/0swtn04/8p05tG/T0FrpTlfwvljOdw== -object-is@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6" - integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY= - object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.0.6: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -7197,7 +6967,7 @@ object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.entries@^1.0.4, object.entries@^1.1.0: +object.entries@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== @@ -7232,7 +7002,7 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.0.4, object.values@^1.1.0: +object.values@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.0.tgz#bf6810ef5da3e5325790eaaa2be213ea84624da9" integrity sha512-8mf0nKLAoFX6VlNVdhGj31SVYpaNFtUnuoOXWyFEstsWRgU837AK+JYM0iAxwkSzGRbwn8cbFmgbyxj1j4VbXg== @@ -7560,13 +7330,6 @@ parse5@5.1.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.0.tgz#c59341c9723f414c452975564c7c00a68d58acd2" integrity sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ== -parse5@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" - integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== - dependencies: - "@types/node" "*" - parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -8151,15 +7914,6 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.3" -prop-types-exact@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" - integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== - dependencies: - has "^1.0.3" - object.assign "^4.1.0" - reflect.ownkeys "^0.2.0" - prop-types@^15.6.2, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" @@ -8253,26 +8007,6 @@ quote-stream@^1.0.1, quote-stream@~1.0.2: minimist "^1.1.3" through2 "^2.0.0" -raf@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" - integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== - dependencies: - performance-now "^2.1.0" - -railroad-diagrams@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e" - integrity sha1-635iZ1SN3t+4mcG5Dlc3RVnN234= - -randexp@0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3" - integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ== - dependencies: - discontinuous-range "1.0.0" - ret "~0.1.10" - randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -8318,12 +8052,12 @@ react-dom@^16.9.0: prop-types "^15.6.2" scheduler "^0.15.0" -react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0: +react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0: version "16.9.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== -react-test-renderer@^16.0.0-0, react-test-renderer@^16.9.0: +react-test-renderer@^16.9.0: version "16.9.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.9.0.tgz#7ed657a374af47af88f66f33a3ef99c9610c8ae9" integrity sha512-R62stB73qZyhrJo7wmCW9jgl/07ai+YzvouvCXIJLBkRlRqLx4j9RqcLEAfNfU3OxTGucqR2Whmn3/Aad6L3hQ== @@ -8447,11 +8181,6 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" -reflect.ownkeys@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" - integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= - reflexbox@^4.0.5: version "4.0.5" resolved "https://registry.yarnpkg.com/reflexbox/-/reflexbox-4.0.5.tgz#63a53702a111d16df6b1f0d6363b86be8ebc1fc6" @@ -8853,14 +8582,6 @@ rollup@^1.20.3: "@types/node" "^12.7.2" acorn "^7.0.0" -rst-selector-parser@^2.2.3: - version "2.2.3" - resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91" - integrity sha1-gbIw6i/MYGbInjRy3nlChdmwPZE= - dependencies: - lodash.flattendeep "^4.4.0" - nearley "^2.7.10" - rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -8959,7 +8680,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0: +"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -9412,15 +9133,6 @@ string.prototype.padend@^3.0.0: es-abstract "^1.4.3" function-bind "^1.0.2" -string.prototype.trim@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.0.tgz#75a729b10cfc1be439543dae442129459ce61e3d" - integrity sha512-9EIjYD/WdlvLpn987+ctkLf0FfvBefOCuiEr2henD8X+7jfwPnyvTdmW8OJhj5p+M0/96mBdynLWkxUr+rHlpg== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.13.0" - function-bind "^1.1.1" - string_decoder@^1.0.0, string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"