From 7e46ffab635e55f4aa32b6cf12954cc841eb49c8 Mon Sep 17 00:00:00 2001 From: Dustan Kasten Date: Wed, 19 Apr 2017 09:27:35 -0400 Subject: [PATCH] Start putting the fiber docs together (#24) --- package.json | 15 +- src/fiber-types/.flowconfig | 0 src/fiber-types/React.js.flow | 5 + .../ReactCompositeComponentTypes.js.flow | 20 ++ src/fiber-types/ReactCoroutine.js.flow | 34 ++ src/fiber-types/ReactElementType.js.flow | 36 ++ src/fiber-types/ReactFiber.js.flow | 124 +++++++ src/fiber-types/ReactFiberReconciler.js.flow | 107 ++++++ src/fiber-types/ReactFiberRoot.js.flow | 30 ++ src/fiber-types/ReactFiberUpdateQueue.js.flow | 30 ++ src/fiber-types/ReactInstanceType.js.flow | 50 +++ src/fiber-types/ReactPortal.js.flow | 24 ++ src/fiber-types/ReactPriorityLevel.js.flow | 28 ++ src/fiber-types/ReactTypeOfSideEffect.js.flow | 27 ++ src/fiber-types/ReactTypeOfWork.js.flow | 29 ++ src/fiber-types/ReactTypes.js.flow | 29 ++ src/fiber-types/package.json | 8 + src/fiber/.flowconfig | 0 src/fiber/HowDoesFiberWork.md | 48 +++ src/fiber/README.md | 22 +- src/fiber/ReactTinyFiber.js | 325 ++++++++++++++++++ src/fiber/ReactTinyTypes.js | 15 + src/fiber/index.js | 4 + src/fiber/package.json | 38 ++ src/fiber/yarn.lock | 102 ++++++ src/stack/mount.js | 3 + src/stack/package.js | 2 +- test.js | 92 +++-- yarn.lock | 316 ++++++++++++++++- 29 files changed, 1505 insertions(+), 58 deletions(-) create mode 100644 src/fiber-types/.flowconfig create mode 100644 src/fiber-types/React.js.flow create mode 100644 src/fiber-types/ReactCompositeComponentTypes.js.flow create mode 100644 src/fiber-types/ReactCoroutine.js.flow create mode 100644 src/fiber-types/ReactElementType.js.flow create mode 100644 src/fiber-types/ReactFiber.js.flow create mode 100644 src/fiber-types/ReactFiberReconciler.js.flow create mode 100644 src/fiber-types/ReactFiberRoot.js.flow create mode 100644 src/fiber-types/ReactFiberUpdateQueue.js.flow create mode 100644 src/fiber-types/ReactInstanceType.js.flow create mode 100644 src/fiber-types/ReactPortal.js.flow create mode 100644 src/fiber-types/ReactPriorityLevel.js.flow create mode 100644 src/fiber-types/ReactTypeOfSideEffect.js.flow create mode 100644 src/fiber-types/ReactTypeOfWork.js.flow create mode 100644 src/fiber-types/ReactTypes.js.flow create mode 100644 src/fiber-types/package.json create mode 100644 src/fiber/.flowconfig create mode 100644 src/fiber/HowDoesFiberWork.md create mode 100644 src/fiber/ReactTinyFiber.js create mode 100644 src/fiber/ReactTinyTypes.js create mode 100644 src/fiber/index.js create mode 100644 src/fiber/package.json create mode 100644 src/fiber/yarn.lock diff --git a/package.json b/package.json index 1876ccb..228bc45 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,10 @@ "description": "A tiny React renderer to demonstrate how to write a renderer.", "main": "./index.js", "scripts": { - "test": "node ./test" + "flow": "pushd src/fiber; flow; popd;", + "test-stack": "node ./test --stack", + "test-fiber": "node ./test --fiber", + "test": "node ./test && node ./test --fiber && npm run flow" }, "repository": { "type": "git", @@ -26,9 +29,17 @@ "url": "https://github.com/iamdustan/tiny-render-renderer/issues" }, "homepage": "https://github.com/iamdustan/tiny-render-renderer", + "babel": { + "plugins": [ + "transform-flow-strip-types" + ] + }, "dependencies": { + "babel-register": "^6.23.0", "fbjs": "^0.8.4", "react": "15.3.x" }, - "devDependencies": {} + "devDependencies": { + "babel-plugin-transform-flow-strip-types": "^6.22.0" + } } diff --git a/src/fiber-types/.flowconfig b/src/fiber-types/.flowconfig new file mode 100644 index 0000000..e69de29 diff --git a/src/fiber-types/React.js.flow b/src/fiber-types/React.js.flow new file mode 100644 index 0000000..00afc21 --- /dev/null +++ b/src/fiber-types/React.js.flow @@ -0,0 +1,5 @@ +/** @flow */ + +export type ReactComponent = typeof React$Component; +export type ReactElement = typeof React$Element; + diff --git a/src/fiber-types/ReactCompositeComponentTypes.js.flow b/src/fiber-types/ReactCompositeComponentTypes.js.flow new file mode 100644 index 0000000..75e1b30 --- /dev/null +++ b/src/fiber-types/ReactCompositeComponentTypes.js.flow @@ -0,0 +1,20 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactCompositeComponentTypes + * @flow + */ + +export type CompositeComponentTypes = 0 | 1 | 2; + +module.exports = { + ImpureClass: 0, + PureClass: 1, + StatelessFunctional: 2, +}; + diff --git a/src/fiber-types/ReactCoroutine.js.flow b/src/fiber-types/ReactCoroutine.js.flow new file mode 100644 index 0000000..2714dca --- /dev/null +++ b/src/fiber-types/ReactCoroutine.js.flow @@ -0,0 +1,34 @@ +/** + * Copyright 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactCoroutine + * @flow + */ + +'use strict'; + +import type { ReactNodeList } from './ReactTypes'; + +type ReifiedYield = { continuation: Object, props: Object }; +type CoroutineHandler = (props: T, yields: Array) => ReactNodeList; + +export type ReactCoroutine = { + $$typeof: Symbol | number, + key: null | string, + children: any, + // This should be a more specific CoroutineHandler + handler: (props: any, yields: Array) => ReactNodeList, + props: any, +}; +export type ReactYield = { + $$typeof: Symbol | number, + key: null | string, + props: Object, + continuation: mixed +}; + diff --git a/src/fiber-types/ReactElementType.js.flow b/src/fiber-types/ReactElementType.js.flow new file mode 100644 index 0000000..7efe931 --- /dev/null +++ b/src/fiber-types/ReactElementType.js.flow @@ -0,0 +1,36 @@ +/** + * Copyright 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + * @providesModule ReactElementType + */ + +'use strict'; + +export type Source = { + fileName: string, + lineNumber: number, +}; + +export type ReactElement = { + $$typeof: any, + type: any, + key: any, + ref: any, + props: any, + _owner: any, // ReactInstance or ReactFiber + + // __DEV__ + _store: { + validated: boolean, + }, + _self: ReactElement, + _shadowChildren: any, + _source: Source, +}; + diff --git a/src/fiber-types/ReactFiber.js.flow b/src/fiber-types/ReactFiber.js.flow new file mode 100644 index 0000000..9f21167 --- /dev/null +++ b/src/fiber-types/ReactFiber.js.flow @@ -0,0 +1,124 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactFiber + * @flow + */ +import type { Source } from './ReactElementType'; +import type { ReactInstance, DebugID } from './ReactInstanceType'; +import type { TypeOfWork } from './ReactTypeOfWork'; +import type { TypeOfSideEffect } from './ReactTypeOfSideEffect'; +import type { PriorityLevel } from './ReactPriorityLevel'; +import type { UpdateQueue } from './ReactFiberUpdateQueue'; + + +// A Fiber is work on a Component that needs to be done or was done. There can +// be more than one per component. +export type Fiber = { + // __DEV__ only + _debugID ?: DebugID, + _debugSource ?: Source | null, + _debugOwner ?: Fiber | ReactInstance | null, // Stack compatible + + // These first fields are conceptually members of an Instance. This used to + // be split into a separate type and intersected with the other Fiber fields, + // but until Flow fixes its intersection bugs, we've merged them into a + // single type. + + // An Instance is shared between all versions of a component. We can easily + // break this out into a separate object to avoid copying so much to the + // alternate versions of the tree. We put this on a single object for now to + // minimize the number of objects created during the initial render. + + // Tag identifying the type of fiber. + tag: TypeOfWork, + + // Unique identifier of this child. + key: null | string, + + // The function/class/module associated with this fiber. + type: any, + + // The local state associated with this fiber. + stateNode: any, + + // Conceptual aliases + // parent : Instance -> return The parent happens to be the same as the + // return fiber since we've merged the fiber and instance. + + // Remaining fields belong to Fiber + + // The Fiber to return to after finishing processing this one. + // This is effectively the parent, but there can be multiple parents (two) + // so this is only the parent of the thing we're currently processing. + // It is conceptually the same as the return address of a stack frame. + return: Fiber | null, + + // Singly Linked List Tree Structure. + child: Fiber | null, + sibling: Fiber | null, + index: number, + + // The ref last used to attach this node. + // I'll avoid adding an owner field for prod and model that as functions. + ref: null | (((handle : mixed) => void) & { _stringRef: ?string }), + + // Input is the data coming into process this fiber. Arguments. Props. + pendingProps: any, // This type will be more specific once we overload the tag. + // TODO: I think that there is a way to merge pendingProps and memoizedProps. + memoizedProps: any, // The props used to create the output. + + // A queue of state updates and callbacks. + updateQueue: UpdateQueue | null, + + // The state used to create the output + memoizedState: any, + + // Effect + effectTag: TypeOfSideEffect, + + // Singly linked list fast path to the next fiber with side-effects. + nextEffect: Fiber | null, + + // The first and last fiber with side-effect within this subtree. This allows + // us to reuse a slice of the linked list when we reuse the work done within + // this fiber. + firstEffect: Fiber | null, + lastEffect: Fiber | null, + + // This will be used to quickly determine if a subtree has no pending changes. + pendingWorkPriority: PriorityLevel, + + // This value represents the priority level that was last used to process this + // component. This indicates whether it is better to continue from the + // progressed work or if it is better to continue from the current state. + progressedPriority: PriorityLevel, + + // If work bails out on a Fiber that already had some work started at a lower + // priority, then we need to store the progressed work somewhere. This holds + // the started child set until we need to get back to working on it. It may + // or may not be the same as the "current" child. + progressedChild: Fiber | null, + + // When we reconcile children onto progressedChild it is possible that we have + // to delete some child fibers. We need to keep track of this side-effects so + // that if we continue later on, we have to include those effects. Deletions + // are added in the reverse order from sibling pointers. + progressedFirstDeletion: Fiber | null, + progressedLastDeletion: Fiber | null, + + // This is a pooled version of a Fiber. Every fiber that gets updated will + // eventually have a pair. There are cases when we can clean up pairs to save + // memory if we need to. + alternate: Fiber | null, + + // Conceptual aliases + // workInProgress : Fiber -> alternate The alternate used for reuse happens + // to be the same as work in progress. + +}; diff --git a/src/fiber-types/ReactFiberReconciler.js.flow b/src/fiber-types/ReactFiberReconciler.js.flow new file mode 100644 index 0000000..7ad8c85 --- /dev/null +++ b/src/fiber-types/ReactFiberReconciler.js.flow @@ -0,0 +1,107 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactFiberReconciler + * @flow + */ + +'use strict'; + +import type { ReactComponent } from './React'; + +import type { Fiber } from './ReactFiber'; +import type { FiberRoot } from './ReactFiberRoot'; +import type { PriorityLevel } from './ReactPriorityLevel'; +import type { ReactNodeList } from './ReactTypes'; + +type Deadline = { + timeRemaining : () => number +}; + +type OpaqueHandle = Fiber; +type OpaqueRoot = FiberRoot; + +export type HostConfig = { + + getRootHostContext(rootContainerInstance : C) : CX, + getChildHostContext(parentHostContext : CX, type : T) : CX, + getPublicInstance(instance : I | TI) : PI, + + createInstance( + type : T, + props : P, + rootContainerInstance : C, + hostContext : CX, + internalInstanceHandle : OpaqueHandle + ) : I, + appendInitialChild(parentInstance : I, child : I | TI) : void, + finalizeInitialChildren(parentInstance : I, type : T, props : P, rootContainerInstance : C) : boolean, + + prepareUpdate( + instance : I, + type : T, + oldProps : P, + newProps : P, + rootContainerInstance : C, + hostContext : CX + ) : null | PL, + commitUpdate( + instance : I, + updatePayload : PL, + type : T, + oldProps : P, + newProps : P, + internalInstanceHandle : OpaqueHandle + ) : void, + commitMount(instance : I, type : T, newProps : P, internalInstanceHandle : OpaqueHandle) : void, + + shouldSetTextContent(props : P) : boolean, + resetTextContent(instance : I) : void, + + createTextInstance( + text : string, + rootContainerInstance : C, + hostContext : CX, + internalInstanceHandle : OpaqueHandle + ) : TI, + commitTextUpdate(textInstance : TI, oldText : string, newText : string) : void, + + appendChild(parentInstance : I | C, child : I | TI) : void, + insertBefore(parentInstance : I | C, child : I | TI, beforeChild : I | TI) : void, + removeChild(parentInstance : I | C, child : I | TI) : void, + + scheduleAnimationCallback(callback : () => void) : number | void, + scheduleDeferredCallback(callback : (deadline : Deadline) => void) : number | void, + + prepareForCommit() : void, + resetAfterCommit() : void, + + useSyncScheduling ?: boolean, +}; + +export type Reconciler = { + createContainer(containerInfo : C) : OpaqueRoot, + updateContainer( + element : ReactNodeList, + container : OpaqueRoot, + parentComponent : ?ReactComponent + ) : void, + performWithPriority(priorityLevel : PriorityLevel, fn : Function) : void, + batchedUpdates(fn : () => A) : A, + unbatchedUpdates(fn : () => A) : A, + syncUpdates(fn : () => A) : A, + deferredUpdates(fn : () => A) : A, + + // Used to extract the return value from the initial render. Legacy API. + getPublicRootInstance(container : OpaqueRoot) : (ReactComponent | TI | I | null), + + // Use for findDOMNode/findHostNode. Legacy API. + findHostInstance(component : Fiber) : I | TI | null, +}; + + diff --git a/src/fiber-types/ReactFiberRoot.js.flow b/src/fiber-types/ReactFiberRoot.js.flow new file mode 100644 index 0000000..82fdec4 --- /dev/null +++ b/src/fiber-types/ReactFiberRoot.js.flow @@ -0,0 +1,30 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactFiberRoot + * @flow + */ + +'use strict'; + +import type { Fiber } from './ReactFiber'; +import type { UpdateQueue } from './ReactFiberUpdateQueue'; + +export type FiberRoot = { + // Any additional information from the host associated with this root. + containerInfo: any, + // The currently active root fiber. This is the mutable root of the tree. + current: Fiber, + // Determines if this root has already been added to the schedule for work. + isScheduled: boolean, + // The work schedule is a linked list. + nextScheduledRoot: ?FiberRoot, + // Linked list of callbacks to call after updates are committed. + callbackList: ?UpdateQueue, +}; + diff --git a/src/fiber-types/ReactFiberUpdateQueue.js.flow b/src/fiber-types/ReactFiberUpdateQueue.js.flow new file mode 100644 index 0000000..1608867 --- /dev/null +++ b/src/fiber-types/ReactFiberUpdateQueue.js.flow @@ -0,0 +1,30 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactFiberUpdateQueue + * @flow + */ + +'use strict'; + +type UpdateQueueNode = { + partialState: any, + callback: ?Function, + callbackWasCalled: boolean, + isReplace: boolean, + next: ?UpdateQueueNode, +}; + +export type UpdateQueue = UpdateQueueNode & { + isForced: boolean, + hasUpdate: boolean, + hasCallback: boolean, + tail: UpdateQueueNode +}; + + diff --git a/src/fiber-types/ReactInstanceType.js.flow b/src/fiber-types/ReactInstanceType.js.flow new file mode 100644 index 0000000..4ccad07 --- /dev/null +++ b/src/fiber-types/ReactInstanceType.js.flow @@ -0,0 +1,50 @@ +/** + * Copyright 2016-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactInstanceType + * @flow + */ + +'use strict'; + +import type {ReactElement} from './ReactElementType'; +import type {CompositeComponentTypes} from './ReactCompositeComponentTypes'; + +export type DebugID = number; + +export type ReactInstance = { + // Shared + mountComponent: any, + unmountComponent: any, + receiveComponent: any, + getName: () => string, + getPublicInstance: any, + _currentElement: ReactElement, + + // ReactCompositeComponent + performInitialMountWithErrorHandling: any, + performInitialMount: any, + getHostNode: any, + performUpdateIfNecessary: any, + updateComponent: any, + attachRef: (ref: string, component: ReactInstance) => void, + detachRef: (ref: string) => void, + _rootNodeID: number, + _compositeType: CompositeComponentTypes, + + // ReactDOMComponent + _tag: string, + + // instantiateReactComponent + _mountIndex: number, + _mountImage: any, + // __DEV__ + _debugID: DebugID, + _warnedAboutRefsInRender: boolean, +}; + diff --git a/src/fiber-types/ReactPortal.js.flow b/src/fiber-types/ReactPortal.js.flow new file mode 100644 index 0000000..d91df12 --- /dev/null +++ b/src/fiber-types/ReactPortal.js.flow @@ -0,0 +1,24 @@ +/** + * Copyright 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactPortal + * @flow + */ + +'use strict'; + +import type { ReactNodeList } from './ReactTypes'; + +export type ReactPortal = { + $$typeof: Symbol | number, + key: null | string, + containerInfo: any, + children : ReactNodeList, + // TODO: figure out the API for cross-renderer implementation. + implementation: any, +}; diff --git a/src/fiber-types/ReactPriorityLevel.js.flow b/src/fiber-types/ReactPriorityLevel.js.flow new file mode 100644 index 0000000..fd4a880 --- /dev/null +++ b/src/fiber-types/ReactPriorityLevel.js.flow @@ -0,0 +1,28 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactPriorityLevel + * @flow + */ + +'use strict'; + +export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5 | 6; + +/* +module.exports = { + NoWork: 0, // No work is pending. + SynchronousPriority: 1, // For controlled text inputs. Synchronous side-effects. + TaskPriority: 2, // Completes at the end of the current tick. + AnimationPriority: 3, // Needs to complete before the next frame. + HighPriority: 4, // Interaction that needs to complete pretty soon to feel responsive. + LowPriority: 5, // Data fetching, or result from updating stores. + OffscreenPriority: 6, // Won't be visible but do the work in case it becomes visible. +}; +*/ + diff --git a/src/fiber-types/ReactTypeOfSideEffect.js.flow b/src/fiber-types/ReactTypeOfSideEffect.js.flow new file mode 100644 index 0000000..4420f39 --- /dev/null +++ b/src/fiber-types/ReactTypeOfSideEffect.js.flow @@ -0,0 +1,27 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactTypeOfSideEffect + * @flow + */ + +'use strict'; + +export type TypeOfSideEffect = 0 | 1 | 2 | 3 | 4 | 8 | 16 | 32 | 64; + +module.exports = { + NoEffect: 0, // 0b0000000 + Placement: 1, // 0b0000001 + Update: 2, // 0b0000010 + PlacementAndUpdate: 3, // 0b0000011 + Deletion: 4, // 0b0000100 + ContentReset: 8, // 0b0001000 + Callback: 16, // 0b0010000 + Err: 32, // 0b0100000 + Ref: 64, // 0b1000000 +}; diff --git a/src/fiber-types/ReactTypeOfWork.js.flow b/src/fiber-types/ReactTypeOfWork.js.flow new file mode 100644 index 0000000..ef3585e --- /dev/null +++ b/src/fiber-types/ReactTypeOfWork.js.flow @@ -0,0 +1,29 @@ +/** + * Copyright 2013-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactTypeOfWork + * @flow + */ + +'use strict'; + +export type TypeOfWork = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10; + +module.exports = { + IndeterminateComponent: 0, // Before we know whether it is functional or class + FunctionalComponent: 1, + ClassComponent: 2, + HostRoot: 3, // Root of a host tree. Could be nested inside another node. + HostPortal: 4, // A subtree. Could be an entry point to a different renderer. + HostComponent: 5, + HostText: 6, + CoroutineComponent: 7, + CoroutineHandlerPhase: 8, + YieldComponent: 9, + Fragment: 10, +}; diff --git a/src/fiber-types/ReactTypes.js.flow b/src/fiber-types/ReactTypes.js.flow new file mode 100644 index 0000000..cba1f67 --- /dev/null +++ b/src/fiber-types/ReactTypes.js.flow @@ -0,0 +1,29 @@ +/** + * Copyright 2014-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule ReactTypes + * @flow + */ + +'use strict'; + +import type { ReactElement } from './React'; + +import type { ReactCoroutine, ReactYield } from './ReactCoroutine'; +import type { ReactPortal } from './ReactPortal'; + +export type ReactNode = ReactElement | ReactCoroutine | ReactYield | ReactPortal | ReactText | ReactFragment; + +export type ReactFragment = ReactEmpty | Iterable; + +export type ReactNodeList = ReactEmpty | ReactNode; + +export type ReactText = string | number; + +export type ReactEmpty = null | void | boolean; + diff --git a/src/fiber-types/package.json b/src/fiber-types/package.json new file mode 100644 index 0000000..b213f6e --- /dev/null +++ b/src/fiber-types/package.json @@ -0,0 +1,8 @@ +{ + "name": "react-fiber-types", + "version": "16.0.0-alpha.3", + "description": "eact is a JavaScript library for building user interfaces", + "main": "ReactFiberReconciler.js", + "repository": "http://github.com/iamdustan/tiny-react-renderer", + "license": "BSD-3-Clause" +} diff --git a/src/fiber/.flowconfig b/src/fiber/.flowconfig new file mode 100644 index 0000000..e69de29 diff --git a/src/fiber/HowDoesFiberWork.md b/src/fiber/HowDoesFiberWork.md new file mode 100644 index 0000000..bfd7c96 --- /dev/null +++ b/src/fiber/HowDoesFiberWork.md @@ -0,0 +1,48 @@ +## How Does Fiber Work? + +> Note: it is highly recommended that you read [https://github.com/acdlite/react-fiber-architecture](https://github.com/acdlite/react-fiber-architecture) +> along with this. Andrew has a lot more foundational definitions and describes +> the `fiber` data structure, whereas this describes how updates are scheduled +> and commited from the renderers point of view. + +The fiber reconciler builds on the idea of Algebraic Effects. A custom renderer +coordinates with the reconciler by informing it when certain effects should be +scheduled. The type of effects are: + +* `NoEffect` +* `Placement` +* `Update` +* `PlacementAndUpdate` +* `Deletion` +* `ContentReset` +* `Callback` +* `Err` +* `Ref` + +This is likely best explained with an example using the DOM renderer. We’ll +create a Composite Component that renders a text instance with the `"Hello"` +inside of a `div` host instance. We’ll immediately render the `App` composite +component updating the text instance contents to be the string `"World"`. + +```jsx +const App = (props) =>
{props.children}
; + +ReactDOM.render(Hello, document.body); +ReactDOM.render(Goodbye, document.body); +``` + +In the initial rendering Fiber will schedule `PlacementAndUpdate` effects to +create the simple `
-> [text#Hello]` tree. When reconciling the second +render call, when beginning work Fiber will schedule a `ContentReset` effect to +clear the text of the `div` and an `Update` effect to schedule setting the new +text content. + +A renderer informs a fiber reconciler what text effects to schedule and how to +complete them through the following handful of methods: + +* `shouldSetTextContent`: communicates to the reconciler if a `ContentReset` + effect should be scheduled +* `resetTextContent`: Fiber calls this method when commiting the `ContentReset` effect +* `createTextInstance`: Fiber calls this method when it needs a new host text instance +* `commitTextUpdate`: Fiber calls this method to commit the new text content `Update` effect + diff --git a/src/fiber/README.md b/src/fiber/README.md index 3110a1e..ae4e523 100644 --- a/src/fiber/README.md +++ b/src/fiber/README.md @@ -1,30 +1,18 @@ # Tiny React Renderer -> Note that this is currently targeting the **React 16.0.0-alpha.2** release. +> Note that this is currently targeting the **React 16.0.0-alpha.3** release. Creating a fiber-based React renderer is quite direct, though there are a few awkward pieces around tooling that will be smoothed over in time. -Many languages have this concept of a `main`—the entry point to your -application. If you look at any React application code you’ve written you’ll see -that you “start” your app with a call like the following: - -```jsx -// web -ReactDOM.render(React.createElement(MyApp), document.getElementById('app')); - -// native -AppRegistry.registerComponent('MyApp', () => MyApp); -``` - -This is where your application enters into the React domain and comes alive. Your -root React element is instantiated and attached to the host environment. +This guide can be read by jumping straight into the code to see the minimal work +to implement a renderer, or you can read the [./HowDoesFiberWork.md](./HowDoesFiberWork.md) +document for additional information on *how* Fiber works. With Fiber, all renderers begin (and maybe even end) in the React{Host}Fiber.js file. -With that let’s get started. Our tour continues in -[./ReactTinyFiber.js](./ReactxTinyFiber.js)! +With that let’s get started in [./ReactTinyFiber.js](./ReactTinyFiber.js)! ## Work in Progress diff --git a/src/fiber/ReactTinyFiber.js b/src/fiber/ReactTinyFiber.js new file mode 100644 index 0000000..425a3bf --- /dev/null +++ b/src/fiber/ReactTinyFiber.js @@ -0,0 +1,325 @@ +/*** + * Welcome to the Tiny React Renderer on Fiber. + * + * The Reconciler API for the current targeted revision is available at: + * https://github.com/facebook/react/blob/ca4325e3eff16b86879188eb996ebcc9a933336a/src/renderers/shared/fiber/ReactFiberReconciler.js#L48-L104 + * + * A renderer is the public interface to a React reconciler. With Fiber you + * create a reconciler by calling `ReactFiberReconciler` with a `HostConfig` + * object. + * + * All types for creating a react reconciler are manually extracted into + * `../react-types` for the current revision (16.0.0-alpha.3). + * + * @flow + */ + +'use strict'; + +/** + * The two internal types you need to be aware of. Everything else should be + * kept private except host-specific things that you handle in your renderer. + */ +import type { HostConfig, Reconciler } from 'react-fiber-types'; +import type { ReactNodeList } from 'react-fiber-types/ReactTypes'; + +// our renconciler types are defined in ./ReactTinyTypes.js for a convenient place to see +// what types you’re expected to define when implementing a renderer +import type { + Props, + Container, + Instance, + TextInstance, + OpaqueHandle, + HostContext, +} from './ReactTinyTypes'; + +/** + * This is the only entry point you need to create a Fiber renderer. Note that + * it currently lives within the `react-dom` package and not `react. + */ +const ReactFiberReconciler : ( + hostConfig: HostConfig<*, *, *, *, *, *, *, *> +) => Reconciler<*, *, *> = require('react-dom/lib/ReactFiberReconciler'); + +const LOG_STEPS = false; +const log = (a, b, c) => { + if (LOG_STEPS) { + console.log(a, b, c); + } +}; + +const toJSON = (node) => { + const props = node.props; + if (typeof props.toJSON === 'function') { + return props.toJSON(props); + } + + let children = null; + if (props.children) { + if (Array.isArray(props.children)) { + children = props.children.map(toJSON); + } else if (props.children) { + children = toJSON(props.children); + } + return Object.assign({}, props, {children}); + } else { + const clone = Object.assign({}, props); + delete clone.children; + return clone; + } +}; + + +/** + * The fun begins! + * + * We create a private reconciler instance. The methods defined here can be + * thought of as the lifecycle of a renderer. React will manage all non-host + * components, such as composites, stateless, and fragments. + */ +const TinyRenderer = ReactFiberReconciler({ + + // the tree creation and updating methods. If you’re familiar with the DOM API + // this will look familiar + + createInstance( + type : string, + props : Props, + rootContainerInstance : Container, + hostContext : HostContext, + internalInstanceHandle : Object + ) { + if (props.toJSON) { + return props.toJSON(props); + } else { + return toJSON({props}); + } + }, + + // this is called instead of `appendChild` when the parentInstance is first + // being created and mounted + // added in https://github.com/facebook/react/pull/8400/ + appendInitialChild( + parentInstance : Instance, + child : Instance | TextInstance + ) : void { + // + log('appendInitialChild', child); + }, + + + appendChild( + parentInstance : Instance | Container, + child : Instance | TextInstance + ) : void { + log('appendChild', child); + // const index = parentInstance.children.indexOf(child); + // if (index !== -1) { + // parentInstance.children.splice(index, 1); + // } + // parentInstance.children.push(child); + }, + + removeChild( + parentInstance : Instance | Container, + child : Instance | TextInstance + ) : void { + log('removeChild', child); + // parentInstance.removeChild(child); + }, + + insertBefore( + parentInstance : Instance | Container, + child : Instance | TextInstance, + beforeChild : Instance | TextInstance + ) : void { + log('insertBefore'); + // parentInstance.insertBefore(child, beforeChild); + }, + + // finalizeInitialChildren is the final HostConfig method called before + // flushing the root component to the host environment + + finalizeInitialChildren( + instance : Instance, + type : string, + props : Props, + rootContainerInstance : Container + ) : boolean { + log('finalizeInitialChildren'); + // setInitialProperties(instance, type, props, rootContainerInstance); + return false; + }, + + // prepare update is where you compute the diff for an instance. This is done + // here to separate computation of the diff to the applying of the diff. Fiber + // can reuse this work even if it pauses or aborts rendering a subset of the + // tree. + + prepareUpdate( + instance : Instance, + type : string, + oldProps : Props, + newProps : Props, + rootContainerInstance : Container, + hostContext : HostContext + ) : null | Array { + log('TODO: prepareUpdate'); + return null; + // return diffProperties(instance, type, oldProps, newProps, rootContainerInstance, hostContext); + }, + + commitUpdate( + instance : Instance, + updatePayload : Array, + type : string, + oldProps : Props, + newProps : Props, + internalInstanceHandle : Object, + ) : void { + // Apply the diff to the DOM node. + // updateProperties(instance, updatePayload, type, oldProps, newProps); + log('TODO: updateProperties'); + }, + + // commitMount is called after initializeFinalChildren *if* + // `initializeFinalChildren` returns true. + + commitMount( + instance : Instance, + type : string, + newProps : Props, + internalInstanceHandle : Object + ) { + log('commitMount'); + // noop + }, + + // HostContext is an internal object or reference for any bookkeeping your + // renderer may need to do based on current location in the tree. In DOM this + // is necessary for calling the correct `document.createElement` calls based + // upon being in an `html`, `svg`, `mathml`, or other context of the tree. + + getRootHostContext(rootContainerInstance : Container) : HostContext { + log('getRootHostContext'); + return emptyObject; + }, + + getChildHostContext(parentHostContext : HostContext, type: string) : HostContext { + log('getChildHostContext'); + return emptyObject; + }, + + // getPublicInstance should be the identity function in 99% of all scenarios. + // It was added to support the `getNodeMock` functionality for the + // TestRenderers. + + getPublicInstance(instance : Instance | TextInstance) { + log('getPublicInstance'); + if (instance == null) { + return null; + } + console.log(instance) + return instance != null && instance.props.toJSON(instance); + }, + + // the prepareForCommit and resetAfterCommit methods are necessary for any + // global side-effects you need to trigger in the host environment. In + // ReactDOM this does things like disable the ReactDOM events to ensure no + // callbacks are fired during DOM manipulations + + prepareForCommit() : void { + log('prepareForCommit'); + // noop + }, + + resetAfterCommit() : void { + log('resetAfterCommit'); + // noop + }, + + // the following four methods are regarding TextInstances. In our example + // renderer we don’t have specific text nodes like the DOM does so we’ll just + // noop all of them. + + shouldSetTextContent(props : Props): boolean { + log('shouldSetTextContent'); + return false + }, + + resetTextContent(instance : Instance) : void { + log('resetTextContent'); + // noop + }, + + createTextInstance( + text : string, + rootContainerInstance : Container, + hostContext : HostContext, + internalInstanceHandle : OpaqueHandle + ) : TextInstance { + log('createTextInstance'); + return null; + }, + + commitTextUpdate( + textInstance : TextInstance, + oldText : string, + newText : string + ) : void { + log('commitTextUpdate'); + // noop + throw new Error('commitTextUpdate should not be called'); + }, + + scheduleAnimationCallback() { + log('scheduleAnimationCallback'); + }, + + scheduleDeferredCallback() { + log('scheduleDeferredCallback'); + }, + + useSyncScheduling: true, +}); + +/** + * Our public renderer. When someone requires your renderer, this is all they + * should have access to. `render` and `unmountComponentAtNode` methods should + * be considered required, though that isn’t strictly true. + */ +const defaultContainer = {}; +const Tiny = { + render( + element : React$Element, + callback : ?Function, + container : any, + ) { + const containerKey = typeof container === 'undefined' ? defaultContainer : container; + let root = roots.get(containerKey); + if (!root) { + root = TinyRenderer.createContainer(containerKey); + roots.set(container, root); + } + + TinyRenderer.updateContainer((element : any), root, null, callback); + return TinyRenderer.getPublicRootInstance(root); + }, + unmountComponentAtNode(container : any) { + const containerKey = typeof container === 'undefined' ? defaultContainer : container; + const root = roots.get(containerKey); + if (root) { + TinyRenderer.updateContainer(null, root, null, () => { + roots.delete(container); + }); + } + }, + // other API methods you may support, such as `renderPortal()` +}; + +const roots = new Map(); +const emptyObject = {}; + +module.exports = Tiny; + diff --git a/src/fiber/ReactTinyTypes.js b/src/fiber/ReactTinyTypes.js new file mode 100644 index 0000000..3ae262f --- /dev/null +++ b/src/fiber/ReactTinyTypes.js @@ -0,0 +1,15 @@ +/* @flow */ + +export type Props = { + children : null | Instance | Array, + toJSON ?: Function +}; +export type Container = {}; +export type Instance = { + type: string | Function, + props: Props, +}; +export type TextInstance = null; +export type OpaqueHandle = Object; +export type HostContext = Object; + diff --git a/src/fiber/index.js b/src/fiber/index.js new file mode 100644 index 0000000..858bce1 --- /dev/null +++ b/src/fiber/index.js @@ -0,0 +1,4 @@ +/** @flow */ + +module.exports = require('./ReactTinyFiber'); + diff --git a/src/fiber/package.json b/src/fiber/package.json new file mode 100644 index 0000000..b24d14d --- /dev/null +++ b/src/fiber/package.json @@ -0,0 +1,38 @@ +{ + "name": "tiny-react-renderer-fiber", + "private": true, + "description": "A tiny React fiber renderer to demonstrate how to write a renderer.", + "main": "./index.js", + "scripts": { + "test": "pushd ../../; npm run test-fiber" + }, + "repository": { + "type": "git", + "url": "https://github.com/iamdustan/tiny-react-renderer" + }, + "files": [ + "*", + "package.json", + "README.md" + ], + "keywords": [ + "react", + "reactjs", + "renderer" + ], + "author": "Dustan Kasten ", + "license": "MIT", + "bugs": { + "url": "https://github.com/iamdustan/tiny-render-renderer/issues" + }, + "homepage": "https://github.com/iamdustan/tiny-render-renderer", + "dependencies": { + "fbjs": "^0.8.4", + "react": "16.0.0-alpha.3", + "react-dom": "16.0.0-alpha.3", + "react-fiber-types": "file:../fiber-types" + }, + "devDependencies": {} +} + + diff --git a/src/fiber/yarn.lock b/src/fiber/yarn.lock new file mode 100644 index 0000000..595934b --- /dev/null +++ b/src/fiber/yarn.lock @@ -0,0 +1,102 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +asap@~2.0.3: + version "2.0.5" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" + +core-js@^1.0.0: + version "1.2.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" + +encoding@^0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" + dependencies: + iconv-lite "~0.4.13" + +fbjs@^0.8.4, fbjs@^0.8.9: + version "0.8.9" + resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.9.tgz#180247fbd347dcc9004517b904f865400a0c8f14" + dependencies: + core-js "^1.0.0" + isomorphic-fetch "^2.1.1" + loose-envify "^1.0.0" + object-assign "^4.1.0" + promise "^7.1.1" + setimmediate "^1.0.5" + ua-parser-js "^0.7.9" + +iconv-lite@~0.4.13: + version "0.4.15" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" + +is-stream@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +isomorphic-fetch@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" + dependencies: + node-fetch "^1.0.1" + whatwg-fetch ">=0.10.0" + +js-tokens@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + +loose-envify@^1.0.0, loose-envify@^1.1.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" + dependencies: + js-tokens "^3.0.0" + +node-fetch@^1.0.1: + version "1.6.3" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" + dependencies: + encoding "^0.1.11" + is-stream "^1.0.1" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +promise@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" + dependencies: + asap "~2.0.3" + +react-dom@16.0.0-alpha.3: + version "16.0.0-alpha.3" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.0.0-alpha.3.tgz#9cf304ea4bcdafe026f29ec2e71373316ec597ab" + dependencies: + fbjs "^0.8.9" + loose-envify "^1.1.0" + object-assign "^4.1.0" + +"react-fiber-types@file:../fiber-types": + version "16.0.0-alpha.3" + +react@16.0.0-alpha.3: + version "16.0.0-alpha.3" + resolved "https://registry.yarnpkg.com/react/-/react-16.0.0-alpha.3.tgz#addfd7ae9d801fd20c6244142354ae0cb7b1fe00" + dependencies: + fbjs "^0.8.9" + loose-envify "^1.1.0" + object-assign "^4.1.0" + +setimmediate@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +ua-parser-js@^0.7.9: + version "0.7.12" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.12.tgz#04c81a99bdd5dc52263ea29d24c6bf8d4818a4bb" + +whatwg-fetch@>=0.10.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.2.tgz#fe294d1d89e36c5be8b3195057f2e4bc74fc980e" diff --git a/src/stack/mount.js b/src/stack/mount.js index 2f57286..fb233a7 100644 --- a/src/stack/mount.js +++ b/src/stack/mount.js @@ -170,7 +170,10 @@ const render = ( } }); ReactUpdates.ReactReconcileTransaction.release(transaction); + }); + + return component.getPublicInstance(); }; // Congratulations! You’ve done it! You have a React renderer! Though so far diff --git a/src/stack/package.js b/src/stack/package.js index 82c01ba..46c2b98 100644 --- a/src/stack/package.js +++ b/src/stack/package.js @@ -4,7 +4,7 @@ "description": "A tiny React stack renderer to demonstrate how to write a renderer.", "main": "./index.js", "scripts": { - "test": "node ./test" + "test": "pushd ../../; node ./test --fiber" }, "repository": { "type": "git", diff --git a/test.js b/test.js index ef47e19..c5db15d 100644 --- a/test.js +++ b/test.js @@ -1,5 +1,7 @@ 'use strict'; +require('babel-register')({}); + const assert = require('assert'); const React = require('react'); const args = process.argv.slice(2); @@ -8,19 +10,25 @@ const TEST_FILE = args[0] === '-f' || args[0] === '--fiber' ? 'fiber' : 'stack'; +console.log('Running %s tests', TEST_FILE); const TinyRenderer = require('./src/' + TEST_FILE); const render = TinyRenderer.render; const toJSON = (props) => { if (props.children) { - let children; + let childRoutes; if (Array.isArray(props.children)) { - children = props.children.map(_ => ({})); + childRoutes = props.children.map(child => ( + typeof child.props.toJSON === 'function' + ? child.props.toJSON(child.props) + : toJSON(child.props) + )); } else { - children = {}; + childRoutes = {}; } - return {children}; + return {path: props.path, childRoutes}; } - return {}; + + return {path: props.path}; }; // mock stateless components @@ -30,73 +38,82 @@ const Page2 = () => React.createElement('div'); // helper for {children} const Route = (path, component, children) => - React.createElement('route', {path: path, component: component, key: path}, children); + React.createElement('Route', {path: path, component: component, key: path}, children); const Rte = (path, component, children) => - React.createElement('route', {path: path, component: component, key: path, toJSON: toJSON}, children); + React.createElement('Route', {path: path, component: component, key: path, toJSON: toJSON}, children); const ok = []; const fail = []; const skipped = []; +const colors = { + green: '\x1b[32m', + red: '\x1b[31m', + reset: '\x1b[37m', +}; + const it = (desc, fn) => { try { fn.call(null); + console.log('%s✓ %s%s', colors.green, colors.reset, desc); ok.push({desc}); } catch (err) { fail.push({desc, err}); + console.log('%s𝘅 %s%s',colors.red, colors.reset, desc); + console.error('%s. Expected\n %j\n to equal\n %j\n', err.name, err.actual, err.expected) } }; it.skip = (desc, fn) => skipped.push({desc}); it('should render with the default toJSON behavior', () => { - render( + const element = render( Route('/', Base, [ Route('/page/1', Page1), Route('/page/2', Page2) - ]), - (element) => { - assert.deepEqual( - element, + ]) + ); + + assert.deepEqual( + element, + { + path: '/', + component: Base, + children: [ + { + path: '/page/1', + component: Page1 + }, { - path: '/', - component: Base, - children: [ - { - path: '/page/1', - component: Page1 - }, - { - path: '/page/2', - component: Page2 - } - ] + path: '/page/2', + component: Page2 } - ); + ] } ); }); it('should render with a custom toJSON method', () => { - render( + const element = render( Rte('/', Base, [ - Rte('/page/1', Page1), + Rte('/page/1', Page1, [Rte('lol')]), Rte('/page/2', Page2) - ]), - (element) => { - assert.deepEqual( - element, - {children: [{}, {}]} - ); + ]) + ); + + assert.deepEqual( + element, + { + path: '/', + childRoutes: [ + {path: '/page/1', childRoutes: [{path: 'lol'}]}, + {path: '/page/2'} + ] } ); }); if (fail.length > 0) { - fail.map(f => { - console.log(f.desc); - console.error('%s. Expected\n %j\n to equal\n %j\n', f.err.name, f.err.actual, f.err.expected) - }); console.log('%s tests passed', ok.length); if (skipped.length) console.log('%s tests skipped', skipped.length); console.log('%s tests failed', fail.length); @@ -106,3 +123,4 @@ if (fail.length > 0) { if (skipped.length) console.log('%s tests skipped', skipped.length); } +console.log(''); diff --git a/yarn.lock b/yarn.lock index 8565705..42a5e25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1,19 +1,207 @@ # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 + + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + asap@~2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" +babel-code-frame@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4" + dependencies: + chalk "^1.1.0" + esutils "^2.0.2" + js-tokens "^3.0.0" + +babel-core@^6.23.0: + version "6.23.1" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.23.1.tgz#c143cb621bb2f621710c220c5d579d15b8a442df" + dependencies: + babel-code-frame "^6.22.0" + babel-generator "^6.23.0" + babel-helpers "^6.23.0" + babel-messages "^6.23.0" + babel-register "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.23.0" + babel-traverse "^6.23.1" + babel-types "^6.23.0" + babylon "^6.11.0" + convert-source-map "^1.1.0" + debug "^2.1.1" + json5 "^0.5.0" + lodash "^4.2.0" + minimatch "^3.0.2" + path-is-absolute "^1.0.0" + private "^0.1.6" + slash "^1.0.0" + source-map "^0.5.0" + +babel-generator@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.23.0.tgz#6b8edab956ef3116f79d8c84c5a3c05f32a74bc5" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.23.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.2.0" + source-map "^0.5.0" + trim-right "^1.0.1" + +babel-helpers@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.23.0.tgz#4f8f2e092d0b6a8808a4bde79c27f1e2ecf0d992" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.23.0" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-syntax-flow@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" + +babel-plugin-transform-flow-strip-types@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" + dependencies: + babel-plugin-syntax-flow "^6.18.0" + babel-runtime "^6.22.0" + +babel-register@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.23.0.tgz#c9aa3d4cca94b51da34826c4a0f9e08145d74ff3" + dependencies: + babel-core "^6.23.0" + babel-runtime "^6.22.0" + core-js "^2.4.0" + home-or-tmp "^2.0.0" + lodash "^4.2.0" + mkdirp "^0.5.1" + source-map-support "^0.4.2" + +babel-runtime@^6.22.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +babel-template@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.23.0.tgz#04d4f270adbb3aa704a8143ae26faa529238e638" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.23.0" + babel-types "^6.23.0" + babylon "^6.11.0" + lodash "^4.2.0" + +babel-traverse@^6.23.0, babel-traverse@^6.23.1: + version "6.23.1" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.23.1.tgz#d3cb59010ecd06a97d81310065f966b699e14f48" + dependencies: + babel-code-frame "^6.22.0" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-types "^6.23.0" + babylon "^6.15.0" + debug "^2.2.0" + globals "^9.0.0" + invariant "^2.2.0" + lodash "^4.2.0" + +babel-types@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.23.0.tgz#bb17179d7538bad38cd0c9e115d340f77e7e9acf" + dependencies: + babel-runtime "^6.22.0" + esutils "^2.0.2" + lodash "^4.2.0" + to-fast-properties "^1.0.1" + +babylon@^6.11.0, babylon@^6.15.0: + version "6.16.1" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.16.1.tgz#30c5a22f481978a9e7f8cdfdf496b11d94b404d3" + +balanced-match@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +brace-expansion@^1.0.0: + version "1.1.6" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.6.tgz#7197d7eaa9b87e648390ea61fc66c84427420df9" + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +chalk@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +convert-source-map@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.4.0.tgz#e3dad195bf61bfe13a7a3c73e9876ec14a0268f3" + core-js@^1.0.0: version "1.2.7" resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" +core-js@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +debug@^2.1.1, debug@^2.2.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.1.tgz#79855090ba2c4e3115cc7d8769491d58f0491351" + dependencies: + ms "0.7.2" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + encoding@^0.1.11: version "0.1.12" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" dependencies: iconv-lite "~0.4.13" +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + fbjs@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.5.tgz#f69ba8a876096cb1b9bffe4d7c1e71c19d39d008" @@ -26,6 +214,23 @@ fbjs@^0.8.4: promise "^7.1.1" ua-parser-js "^0.7.9" +globals@^9.0.0: + version "9.16.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.16.0.tgz#63e903658171ec2d9f51b1d31de5e2b8dc01fb80" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + iconv-lite@~0.4.13: version "0.4.13" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.13.tgz#1f88aba4ab0b1508e8312acc39345f36e992e2f2" @@ -34,6 +239,18 @@ immutable@^3.7.6: version "3.8.1" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.1.tgz#200807f11ab0f72710ea485542de088075f68cd2" +invariant@^2.2.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360" + dependencies: + loose-envify "^1.0.0" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + is-stream@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -49,12 +266,48 @@ js-tokens@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.3.tgz#14e56eb68c8f1a92c43d59f5014ec29dc20f2ae1" +js-tokens@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +json5@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +lodash@^4.2.0: + version "4.17.4" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" + loose-envify@^1.0.0, loose-envify@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.2.0.tgz#69a65aad3de542cf4ee0f4fe74e8e33c709ccb0f" dependencies: js-tokens "^1.0.1" +minimatch@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + dependencies: + brace-expansion "^1.0.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +ms@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + node-fetch@^1.0.1: version "1.6.3" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04" @@ -62,10 +315,30 @@ node-fetch@^1.0.1: encoding "^0.1.11" is-stream "^1.0.1" +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + object-assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0" +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +private@^0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.7.tgz#68ce5e8a1ef0a23bb570cc28537b5332aba63ef1" + promise@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.1.1.tgz#489654c692616b8aa55b0724fa809bb7db49c5bf" @@ -80,6 +353,48 @@ react@15.3.x: loose-envify "^1.1.0" object-assign "^4.1.0" +regenerator-runtime@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +source-map-support@^0.4.2: + version "0.4.11" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.11.tgz#647f939978b38535909530885303daf23279f322" + dependencies: + source-map "^0.5.3" + +source-map@^0.5.0, source-map@^0.5.3: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +strip-ansi@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +to-fast-properties@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + ua-parser-js@^0.7.9: version "0.7.10" resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.10.tgz#917559ddcce07cbc09ece7d80495e4c268f4ef9f" @@ -87,4 +402,3 @@ ua-parser-js@^0.7.9: whatwg-fetch@>=0.10.0: version "1.0.0" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-1.0.0.tgz#01c2ac4df40e236aaa18480e3be74bd5c8eb798e" -