UNPKG

rax

Version:

A universal React-compatible render engine.

1 lines 122 kB
{"version":3,"file":"rax.min.mjs","sources":["../src/vdom/host.js","../src/vdom/element.js","../src/types.js","../src/vdom/flattenChildren.js","../src/vdom/scheduler.js","../src/error.js","../src/createElement.js","../src/invokeFunctionsWithContext.js","../src/vdom/shallowEqual.js","../src/constant.js","../src/hooks.js","../src/toArray.js","../src/vdom/getNearestParent.js","../src/createContext.js","../src/createRef.js","../src/forwardRef.js","../src/memo.js","../src/fragment.js","../src/vdom/instantiateComponent.js","../src/vdom/component.js","../src/vdom/root.js","../src/vdom/instance.js","../src/vdom/ref.js","../src/vdom/shouldUpdateComponent.js","../src/vdom/getElementKeyName.js","../src/vdom/getPrevSiblingNativeNode.js","../src/vdom/base.js","../src/assign.js","../src/vdom/native.js","../src/vdom/reactive.js","../src/vdom/updater.js","../src/vdom/performInSandbox.js","../src/vdom/composite.js","../src/vdom/text.js","../src/vdom/fragment.js","../src/vdom/empty.js","../src/render.js","../src/vdom/injectRenderOptions.js","../src/vdom/inject.js","../src/version.js","../src/index.js"],"sourcesContent":["/*\n * Stateful things in runtime\n */\nexport default {\n __mountID: 1,\n __isUpdating: false,\n // Inject\n driver: null,\n // Roots\n rootComponents: {},\n rootInstances: {},\n // Current owner component\n owner: null,\n};\n","import checkPropTypes from 'prop-types/checkPropTypes';\n\nexport default function Element(type, key, ref, props, owner) {\n let element = {\n // Built-in properties that belong on the element\n type,\n key,\n ref,\n props,\n // Record the component responsible for creating this element.\n _owner: owner,\n };\n\n if (process.env.NODE_ENV !== 'production') {\n const propTypes = type.propTypes;\n\n // Validate its props provided by the propTypes definition\n if (propTypes) {\n const displayName = type.displayName || type.name;\n checkPropTypes(\n propTypes,\n props,\n 'prop',\n displayName,\n );\n }\n\n // We make validation flag non-enumerable, so the test framework could ignore it\n Object.defineProperty(element, '__validated', {\n configurable: false,\n enumerable: false,\n writable: true,\n value: false\n });\n\n // Props is immutable\n if (Object.freeze) {\n Object.freeze(props);\n }\n }\n\n return element;\n};\n","export function isNull(obj) {\n return obj === null;\n}\n\nexport function isFunction(obj) {\n return typeof obj === 'function';\n}\n\nexport function isObject(obj) {\n return typeof obj === 'object';\n}\n\nexport function isPlainObject(obj) {\n return EMPTY_OBJECT.toString.call(obj) === '[object Object]';\n}\n\nexport function isArray(array) {\n return Array.isArray(array);\n}\n\nexport function isString(string) {\n return typeof string === 'string';\n}\n\nexport function isNumber(string) {\n return typeof string === 'number';\n}\n\nexport function isFalsy(val) {\n return !Boolean(val);\n}\n\nexport const NOOP = () => {};\nexport const EMPTY_OBJECT = {};\n","import { isArray } from '../types';\n\nfunction traverseChildren(children, result) {\n if (isArray(children)) {\n for (let i = 0, l = children.length; i < l; i++) {\n traverseChildren(children[i], result);\n }\n } else {\n result.push(children);\n }\n}\n\nexport default function flattenChildren(children) {\n if (children == null) {\n return children;\n }\n const result = [];\n traverseChildren(children, result);\n\n // If length equal 1, return the only one.\n return result.length - 1 ? result : result[0];\n}\n","let updateCallbacks = [];\nlet effectCallbacks = [];\nlet layoutCallbacks = [];\nexport let scheduler = setTimeout;\n\nif (process.env.NODE_ENV !== 'production') {\n // Wrapper timer for hijack timers in jest\n scheduler = (callback) => {\n setTimeout(callback);\n };\n}\n\nfunction invokeFunctionsWithClear(callbacks) {\n let callback;\n while (callback = callbacks.shift()) {\n callback();\n }\n}\n\n// Schedule before next render\nexport function schedule(callback) {\n if (updateCallbacks.length === 0) {\n scheduler(flush);\n }\n updateCallbacks.push(callback);\n}\n\n// Flush before next render\nexport function flush() {\n invokeFunctionsWithClear(updateCallbacks);\n}\n\nexport function scheduleEffect(callback) {\n if (effectCallbacks.length === 0) {\n scheduler(flushEffect);\n }\n effectCallbacks.push(callback);\n}\n\nexport function flushEffect() {\n invokeFunctionsWithClear(effectCallbacks);\n}\n\nexport function scheduleLayout(callback) {\n layoutCallbacks.push(callback);\n}\n\nexport function flushLayout() {\n invokeFunctionsWithClear(layoutCallbacks);\n}\n","import Host from './vdom/host';\nimport { scheduler } from './vdom/scheduler';\nimport { NOOP, isPlainObject } from './types';\n\nfunction createMinifiedError(type, code, obj) {\n var typeInfo = obj === undefined ? '' : ' got: ' + getTypeInfo(obj);\n return new Error(`${type}: #${code}, ${getRenderErrorInfo()}.` + typeInfo);\n}\n\nexport function getTypeInfo(obj) {\n return isPlainObject(obj) ? Object.keys(obj) : obj;\n}\n\nexport function getRenderErrorInfo() {\n const ownerComponent = Host.owner;\n return ownerComponent ? `check <${ownerComponent.__getName()}>` : 'no owner';\n}\n\n/**\n * Minified code:\n * 1: Hooks called outside a component, or multiple version of Rax are used.\n * 6: Invalid component type, expected a class or function component.\n * 4: Too many re-renders, the number of renders is limited to prevent an infinite loop.\n * 5: Rax driver not found.\n * @param code {Number}\n * @param obj {Object}\n */\nexport function throwMinifiedError(code, obj) {\n throw createMinifiedError('Error', code, obj);\n}\n\n/**\n * Minified Code:\n * 0: Invalid element type, expected a string or a class/function component but got \"null\" or \"undefined\".\n * 2. Invalid child type, expected types: Element instance, string, boolean, array, null, undefined.\n * 3. Ref can not attach because multiple copies of Rax are used.\n * @param {number} code\n * @param {string} info\n */\nexport function throwMinifiedWarn(code, obj) {\n let err = createMinifiedError('Warn', code, obj);\n scheduler(() => {\n throw err;\n }, 0);\n}\n\nexport function throwError(message, obj) {\n let typeInfo = obj === undefined ? '' :\n '(found: ' + (isPlainObject(obj) ? `object with keys {${Object.keys(obj)}}` : obj) + ')';\n\n throw Error(`${message} ${typeInfo}`);\n}\n\nexport let warning = NOOP;\n\nif (process.env.NODE_ENV !== 'production') {\n warning = (template, ...args) => {\n if (typeof console !== 'undefined') {\n let argsWithFormat = args.map(item => '' + item);\n argsWithFormat.unshift('Warning: ' + template);\n // Don't use spread (or .apply) directly because it breaks IE9\n Function.prototype.apply.call(console.error, console, argsWithFormat);\n }\n\n // For works in DevTools when enable `Pause on caught exceptions`\n // that can find the component where caused this warning\n try {\n let argIndex = 0;\n const message = 'Warning: ' + template.replace(/%s/g, () => args[argIndex++]);\n throw new Error(message);\n } catch (e) {}\n };\n}\n\n","import Host from './vdom/host';\nimport Element from './vdom/element';\nimport flattenChildren from './vdom/flattenChildren';\nimport { warning, throwError, throwMinifiedWarn } from './error';\nimport { isString, isArray, NOOP } from './types';\nimport validateChildKeys from './validateChildKeys';\n\nconst RESERVED_PROPS = {\n key: true,\n ref: true,\n};\n\nexport default function createElement(type, config, children) {\n // Reserved names are extracted\n let props = {};\n let propName;\n let key = null;\n let ref = null;\n\n if (config != null) {\n ref = config.ref === undefined ? null : config.ref;\n key = config.key === undefined ? null : '' + config.key;\n\n // Remaining properties are added to a new props object\n for (propName in config) {\n if (!RESERVED_PROPS[propName]) {\n props[propName] = config[propName];\n }\n }\n }\n\n // Children arguments can be more than one\n const childrenLength = arguments.length - 2;\n if (childrenLength > 0) {\n if (childrenLength === 1 && !isArray(children)) {\n props.children = children;\n } else {\n let childArray = children;\n if (childrenLength > 1) {\n childArray = new Array(childrenLength);\n for (var i = 0; i < childrenLength; i++) {\n childArray[i] = arguments[i + 2];\n }\n }\n props.children = flattenChildren(childArray);\n }\n }\n\n // Resolve default props\n if (type && type.defaultProps) {\n let defaultProps = type.defaultProps;\n for (propName in defaultProps) {\n if (props[propName] === undefined) {\n props[propName] = defaultProps[propName];\n }\n }\n }\n\n if (type == null) {\n if (process.env.NODE_ENV !== 'production') {\n throwError(`Invalid element type, expected a string or a class/function component but got \"${type}\".`);\n } else {\n // A empty component replaced avoid break render in production\n type = NOOP;\n throwMinifiedWarn(0);\n }\n }\n\n if (process.env.NODE_ENV !== 'production') {\n if (isString(ref) && !Host.owner) {\n warning(\n `Adding a string ref \"${ref}\" that was not created inside render method, or multiple copies of Rax are used.`\n );\n }\n\n for (let i = 2; i < arguments.length; i ++) {\n validateChildKeys(arguments[i], type);\n }\n }\n\n return new Element(\n type,\n key,\n ref,\n props,\n Host.owner\n );\n}\n\n","export default function invokeFunctionsWithContext(fns, context, value) {\n for (let i = 0, l = fns && fns.length; i < l; i++) {\n fns[i].call(context, value);\n }\n}\n","import { isNull, isObject, EMPTY_OBJECT } from '../types';\n\nconst hasOwnProperty = EMPTY_OBJECT.hasOwnProperty;\n\n/**\n * inlined Object.is polyfill to avoid requiring consumers ship their own\n * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is\n */\nexport function is(x, y) {\n // SameValue algorithm\n if (x === y) {\n // Steps 1-5, 7-10\n // Steps 6.b-6.e: +0 != -0\n return x !== 0 || 1 / x === 1 / y;\n } else {\n // Step 6.a: NaN == NaN\n return x !== x && y !== y; // eslint-disable-line no-self-compare\n }\n}\n\n/**\n * Performs equality by iterating through keys on an object and returning false\n * when any key has values which are not strictly equal between the arguments.\n * Returns true when the values of all keys are strictly equal.\n */\nexport default function shallowEqual(objA, objB) {\n if (is(objA, objB)) {\n return true;\n }\n\n if (!isObject(objA) || isNull(objA) || !isObject(objB) || isNull(objB)) {\n return false;\n }\n\n let keysA = Object.keys(objA);\n let keysB = Object.keys(objB);\n\n if (keysA.length !== keysB.length) {\n return false;\n }\n\n // Test for A's keys different from B.\n for (let i = 0; i < keysA.length; i++) {\n if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {\n return false;\n }\n }\n\n return true;\n}\n","/* Common constant variables for rax */\n\nexport const INTERNAL = '_internal';\nexport const INSTANCE = '_instance';\nexport const NATIVE_NODE = '_nativeNode';\nexport const RENDERED_COMPONENT = '_renderedComponent';\n","import Host from './vdom/host';\nimport { scheduleEffect, flushEffect } from './vdom/scheduler';\nimport { is } from './vdom/shallowEqual';\nimport { isArray, isFunction, isNull } from './types';\nimport { warning, throwError, throwMinifiedError } from './error';\nimport { INSTANCE } from './constant';\n\nfunction getCurrentInstance() {\n return Host.owner && Host.owner[INSTANCE];\n}\n\nfunction getCurrentRenderingInstance() {\n const currentInstance = getCurrentInstance();\n if (currentInstance) {\n return currentInstance;\n } else {\n if (process.env.NODE_ENV !== 'production') {\n throwError('Hooks called outside a component, or multiple version of Rax are used.');\n } else {\n throwMinifiedError(1);\n }\n }\n}\n\nfunction areInputsEqual(inputs, prevInputs) {\n if (isNull(prevInputs) || inputs.length !== prevInputs.length) {\n return false;\n }\n\n for (let i = 0; i < inputs.length; i++) {\n if (is(inputs[i], prevInputs[i])) {\n continue;\n }\n return false;\n }\n return true;\n}\n\nexport function useState(initialState) {\n const currentInstance = getCurrentRenderingInstance();\n const hookID = currentInstance.getHookID();\n const hooks = currentInstance.getHooks();\n\n if (!hooks[hookID]) {\n // If the initial state is the result of an expensive computation,\n // you may provide a function instead for lazy initial state.\n if (isFunction(initialState)) {\n initialState = initialState();\n }\n\n const setState = newState => {\n // Flush all effects first before update state\n if (!Host.__isUpdating) {\n flushEffect();\n }\n\n const hook = hooks[hookID];\n const eagerState = hook[2];\n // function updater\n if (isFunction(newState)) {\n newState = newState(eagerState);\n }\n\n if (!is(newState, eagerState)) {\n // Current instance is in render update phase.\n // After this one render finish, will continue run.\n hook[2] = newState;\n if (getCurrentInstance() === currentInstance) {\n // Marked as is scheduled that could finish hooks.\n currentInstance.__isScheduled = true;\n } else {\n currentInstance.__update();\n }\n }\n };\n\n hooks[hookID] = [\n initialState,\n setState,\n initialState\n ];\n }\n\n const hook = hooks[hookID];\n if (!is(hook[0], hook[2])) {\n hook[0] = hook[2];\n currentInstance.__shouldUpdate = true;\n }\n\n return hook;\n}\n\nexport function useContext(context) {\n const currentInstance = getCurrentRenderingInstance();\n return currentInstance.useContext(context);\n}\n\nexport function useEffect(effect, inputs) {\n useEffectImpl(effect, inputs, true);\n}\n\nexport function useLayoutEffect(effect, inputs) {\n useEffectImpl(effect, inputs);\n}\n\nfunction useEffectImpl(effect, inputs, defered) {\n const currentInstance = getCurrentRenderingInstance();\n const hookID = currentInstance.getHookID();\n const hooks = currentInstance.getHooks();\n inputs = inputs === undefined ? null : inputs;\n\n if (!hooks[hookID]) {\n const __create = (immediately) => {\n if (!immediately && defered) return scheduleEffect(() => __create(true));\n const { current } = __create;\n if (current) {\n __destory.current = current();\n __create.current = null;\n\n if (process.env.NODE_ENV !== 'production') {\n const currentDestory = __destory.current;\n if (currentDestory !== undefined && typeof currentDestory !== 'function') {\n let msg;\n if (currentDestory === null) {\n msg =\n ' You returned null. If your effect does not require clean ' +\n 'up, return undefined (or nothing).';\n } else if (typeof currentDestory.then === 'function') {\n msg =\n '\\n\\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' +\n 'Instead, write the async function inside your effect ' +\n 'and call it immediately:\\n\\n' +\n 'useEffect(() => {\\n' +\n ' async function fetchData() {\\n' +\n ' // You can await here\\n' +\n ' const response = await MyAPI.getData(someId);\\n' +\n ' // ...\\n' +\n ' }\\n' +\n ' fetchData();\\n' +\n '}, [someId]); // Or [] if effect doesn\\'t need props or state.';\n } else {\n msg = ' You returned: ' + currentDestory;\n }\n\n warning(\n 'An effect function must not return anything besides a function, ' +\n 'which is used for clean-up.' + msg,\n );\n }\n }\n }\n };\n\n const __destory = (immediately) => {\n if (!immediately && defered) return scheduleEffect(() => __destory(true));\n const { current } = __destory;\n if (current) {\n current();\n __destory.current = null;\n }\n };\n\n __create.current = effect;\n\n hooks[hookID] = {\n __create,\n __destory,\n __prevInputs: inputs,\n __inputs: inputs\n };\n\n currentInstance.didMount.push(__create);\n currentInstance.willUnmount.push(() => __destory(true));\n currentInstance.didUpdate.push(() => {\n const { __prevInputs, __inputs, __create } = hooks[hookID];\n if (__inputs == null || !areInputsEqual(__inputs, __prevInputs)) {\n __destory();\n __create();\n }\n });\n } else {\n const hook = hooks[hookID];\n const { __create, __inputs: prevInputs } = hook;\n hook.__inputs = inputs;\n hook.__prevInputs = prevInputs;\n __create.current = effect;\n }\n}\n\nexport function useImperativeHandle(ref, create, inputs) {\n const nextInputs = isArray(inputs) ? inputs.concat([ref]) : null;\n\n useLayoutEffect(() => {\n if (isFunction(ref)) {\n ref(create());\n return () => ref(null);\n } else if (ref != null) {\n ref.current = create();\n return () => {\n ref.current = null;\n };\n }\n }, nextInputs);\n}\n\nexport function useRef(initialValue) {\n const currentInstance = getCurrentRenderingInstance();\n const hookID = currentInstance.getHookID();\n const hooks = currentInstance.getHooks();\n\n if (!hooks[hookID]) {\n hooks[hookID] = {\n current: initialValue\n };\n }\n\n return hooks[hookID];\n}\n\nexport function useCallback(callback, inputs) {\n return useMemo(() => callback, inputs);\n}\n\nexport function useMemo(create, inputs) {\n const currentInstance = getCurrentRenderingInstance();\n const hookID = currentInstance.getHookID();\n const hooks = currentInstance.getHooks();\n inputs = inputs === undefined ? null : inputs;\n\n if (!hooks[hookID]) {\n hooks[hookID] = [create(), inputs];\n } else {\n const prevInputs = hooks[hookID][1];\n if (isNull(inputs) || !areInputsEqual(inputs, prevInputs)) {\n hooks[hookID] = [create(), inputs];\n }\n }\n\n return hooks[hookID][0];\n}\n\nexport function useReducer(reducer, initialArg, init) {\n const currentInstance = getCurrentRenderingInstance();\n const hookID = currentInstance.getHookID();\n const hooks = currentInstance.getHooks();\n const hook = hooks[hookID];\n\n if (!hook) {\n const initialState = isFunction(init) ? init(initialArg) : initialArg;\n\n const dispatch = action => {\n // Flush all effects first before update state\n if (!Host.__isUpdating) {\n flushEffect();\n }\n\n const hook = hooks[hookID];\n // Reducer will update in the next render, before that we add all\n // actions to the queue\n const queue = hook[2];\n\n if (getCurrentInstance() === currentInstance) {\n queue.__actions.push(action);\n currentInstance.__isScheduled = true;\n } else {\n const currentState = queue.__eagerState;\n const eagerReducer = queue.__eagerReducer;\n const eagerState = eagerReducer(currentState, action);\n if (is(eagerState, currentState)) {\n return;\n }\n queue.__eagerState = eagerState;\n queue.__actions.push(action);\n currentInstance.__update();\n }\n };\n\n return hooks[hookID] = [\n initialState,\n dispatch,\n {\n __actions: [],\n __eagerReducer: reducer,\n __eagerState: initialState\n }\n ];\n }\n\n const queue = hook[2];\n let next = hook[0];\n\n if (currentInstance.__reRenders > 0) {\n for (let i = 0; i < queue.__actions.length; i++) {\n next = reducer(next, queue.__actions[i]);\n }\n } else {\n next = queue.__eagerState;\n }\n\n if (!is(next, hook[0])) {\n hook[0] = next;\n currentInstance.__shouldUpdate = true;\n }\n\n queue.__eagerReducer = reducer;\n queue.__eagerState = next;\n queue.__actions.length = 0;\n\n return hooks[hookID];\n}\n","import { isArray } from './types';\n\nexport default function toArray(obj) {\n return isArray(obj) ? obj : [obj];\n}\n","import { INTERNAL } from '../constant';\n\nexport default function getNearestParent(instance, matcher) {\n let parent;\n while (instance && instance[INTERNAL]) {\n if (matcher(instance)) {\n parent = instance;\n break;\n }\n instance = instance[INTERNAL].__parentInstance;\n }\n return parent;\n}","import invokeFunctionsWithContext from './invokeFunctionsWithContext';\nimport { useState, useLayoutEffect } from './hooks';\nimport { isFunction } from './types';\nimport toArray from './toArray';\nimport getNearestParent from './vdom/getNearestParent';\n\nlet id = 0;\n\nexport default function createContext(defaultValue) {\n const contextID = '_c' + id++;\n\n // Provider Component\n class Provider {\n constructor() {\n this.__contextID = contextID;\n this.__handlers = [];\n }\n __on(handler) {\n this.__handlers.push(handler);\n }\n __off(handler) {\n this.__handlers = this.__handlers.filter(h => h !== handler);\n }\n // Like getChildContext but called in SSR\n _getChildContext() {\n return {\n [contextID]: this\n };\n }\n // `getValue()` called in rax-server-renderer\n getValue() {\n return this.props.value !== undefined ? this.props.value : defaultValue;\n }\n componentDidUpdate(prevProps) {\n if (this.props.value !== prevProps.value) {\n invokeFunctionsWithContext(this.__handlers, null, this.getValue());\n }\n }\n render() {\n return this.props.children;\n }\n }\n\n function getNearestParentProvider(instance) {\n return getNearestParent(instance, parent => parent.__contextID === contextID);\n }\n\n // Consumer Component\n function Consumer(props, context) {\n // Current `context[contextID]` only works in SSR\n const [provider] = useState(() => context[contextID] || getNearestParentProvider(this));\n let value = provider ? provider.getValue() : defaultValue;\n const [prevValue, setValue] = useState(value);\n\n if (value !== prevValue) {\n setValue(value);\n return; // Interrupt execution of consumer.\n }\n\n useLayoutEffect(() => {\n if (provider) {\n provider.__on(setValue);\n return () => {\n provider.__off(setValue);\n };\n }\n }, []);\n\n // Consumer requires a function as a child.\n // The function receives the current context value.\n const consumer = toArray(props.children)[0];\n if (isFunction(consumer)) {\n return consumer(value);\n }\n }\n\n return {\n Provider,\n Consumer,\n // `_contextID` and `_defaultValue` accessed in rax-server-renderer\n _contextID: contextID,\n _defaultValue: defaultValue,\n __getNearestParentProvider: getNearestParentProvider,\n };\n}\n","export default function createRef() {\n return {\n current: null\n };\n}","export default function(render) {\n // _forwardRef is also use in rax server renderer\n render._forwardRef = true;\n return render;\n}","import shallowEqual from './vdom/shallowEqual';\n\nexport default function memo(type, compare) {\n compare = compare || shallowEqual;\n\n // Memo could composed\n if (type.__compares) {\n type.__compares.push(compare);\n } else {\n type.__compares = [compare];\n }\n\n return type;\n}\n","export default function Fragment(props) {\n return props.children;\n}","import Host from './host';\nimport {isString, isNumber, isArray, isNull, isPlainObject} from '../types';\nimport { throwMinifiedWarn, throwError } from '../error';\n\nexport default function instantiateComponent(element) {\n let instance;\n\n if (isPlainObject(element) && element !== null && element.type) {\n // Special case string values\n if (isString(element.type)) {\n instance = new Host.__Native(element);\n } else {\n instance = new Host.__Composite(element);\n }\n } else if (isString(element) || isNumber(element)) {\n instance = new Host.__Text(String(element));\n } else if (isArray(element)) {\n instance = new Host.__Fragment(element);\n } else {\n if (!(element === undefined || isNull(element) || element === false || element === true)) {\n if (process.env.NODE_ENV !== 'production') {\n throwError('Invalid child type, expected types: Element instance, string, boolean, array, null, undefined.', element);\n } else {\n throwMinifiedWarn(2, element);\n }\n }\n\n instance = new Host.__Empty(element);\n }\n\n return instance;\n}\n","/**\n * Base component class.\n */\nexport default class Component {\n constructor(props, context) {\n this.props = props;\n this.context = context;\n this.refs = {};\n }\n\n setState(partialState, callback) {\n // The updater property is injected when composite component mounting\n this.updater.setState(this, partialState, callback);\n }\n\n forceUpdate(callback) {\n this.updater.forceUpdate(this, callback);\n }\n}\n\n/**\n * Pure component.\n */\nexport class PureComponent extends Component {\n constructor(props, context) {\n super(props, context);\n this.__isPureComponent = true;\n }\n}\n","import Component from './component';\nimport {INTERNAL, RENDERED_COMPONENT} from '../constant';\n\nlet rootID = 1;\n\nclass Root extends Component {\n constructor() {\n super();\n // Using fragment instead of null for avoid create a comment node when init mount\n this.__element = [];\n this.__rootID = rootID++;\n }\n\n __getPublicInstance() {\n return this.__getRenderedComponent().__getPublicInstance();\n }\n\n __getRenderedComponent() {\n return this[INTERNAL][RENDERED_COMPONENT];\n }\n\n __update(element) {\n this.__element = element;\n this.forceUpdate();\n }\n\n render() {\n return this.__element;\n }\n}\n\nexport default Root;\n","import Host from './host';\nimport createElement from '../createElement';\nimport instantiateComponent from './instantiateComponent';\nimport Root from './root';\nimport {INTERNAL, RENDERED_COMPONENT} from '../constant';\n\n/**\n * Instance manager\n * @NOTE Key should not be compressed, for that will be added to native node and cause DOM Exception.\n */\nconst KEY = '_r';\n\nexport default {\n set(node, instance) {\n if (!node[KEY]) {\n node[KEY] = instance;\n // Record root instance to roots map\n if (instance.__rootID) {\n Host.rootInstances[instance.__rootID] = instance;\n Host.rootComponents[instance.__rootID] = instance[INTERNAL];\n }\n }\n },\n get(node) {\n return node[KEY];\n },\n remove(node) {\n let instance = this.get(node);\n if (instance) {\n node[KEY] = null;\n if (instance.__rootID) {\n delete Host.rootComponents[instance.__rootID];\n delete Host.rootInstances[instance.__rootID];\n }\n }\n },\n mount(element, container, { parent, hydrate }) {\n if (process.env.NODE_ENV !== 'production') {\n Host.measurer && Host.measurer.beforeRender();\n }\n\n const driver = Host.driver;\n\n // Real native root node is body\n if (container == null) {\n container = driver.createBody();\n }\n\n const renderOptions = {\n element,\n container,\n hydrate\n };\n\n // Before render callback\n driver.beforeRender && driver.beforeRender(renderOptions);\n\n // Get the context from the conceptual parent component.\n let parentContext;\n if (parent) {\n let parentInternal = parent[INTERNAL];\n parentContext = parentInternal.__processChildContext(parentInternal._context);\n }\n\n // Update root component\n let prevRootInstance = this.get(container);\n if (prevRootInstance && prevRootInstance.__rootID) {\n if (parentContext) {\n // Using __penddingContext to pass new context\n prevRootInstance[INTERNAL].__penddingContext = parentContext;\n }\n prevRootInstance.__update(element);\n\n // After render callback\n driver.afterRender && driver.afterRender(renderOptions);\n\n return prevRootInstance;\n }\n\n // Init root component with empty children\n let renderedComponent = instantiateComponent(createElement(Root));\n let defaultContext = parentContext || {};\n let rootInstance = renderedComponent.__mountComponent(container, parent, defaultContext);\n this.set(container, rootInstance);\n\n // Mount new element through update queue avoid when there is in rendering phase\n rootInstance.__update(element);\n\n // After render callback\n driver.afterRender && driver.afterRender(renderOptions);\n\n if (process.env.NODE_ENV !== 'production') {\n // Devtool render new root hook\n Host.reconciler.renderNewRootComponent(rootInstance[INTERNAL][RENDERED_COMPONENT]);\n Host.measurer && Host.measurer.afterRender();\n }\n\n return rootInstance;\n }\n};\n","/*\n * Ref manager\n */\nimport { isFunction, isObject } from '../types';\nimport { INSTANCE } from '../constant';\nimport { warning, throwMinifiedWarn } from '../error';\n\nexport function updateRef(prevElement, nextElement, component) {\n let prevRef = prevElement ? prevElement.ref : null;\n let nextRef = nextElement ? nextElement.ref : null;\n\n // Update refs in owner component\n if (prevRef !== nextRef) {\n // Detach prev RenderedElement's ref\n prevRef && detachRef(prevElement._owner, prevRef, component);\n // Attach next RenderedElement's ref\n nextRef && attachRef(nextElement._owner, nextRef, component);\n }\n}\n\nexport function attachRef(ownerComponent, ref, component) {\n if (!ownerComponent) {\n if (process.env.NODE_ENV !== 'production') {\n warning('Ref can not attach because multiple copies of Rax are used.');\n } else {\n throwMinifiedWarn(3);\n }\n return;\n }\n\n let instance = component.__getPublicInstance();\n\n if (process.env.NODE_ENV !== 'production') {\n if (instance == null) {\n warning('Do not attach ref to function component because they don’t have instances.');\n }\n }\n\n if (isFunction(ref)) {\n ref(instance);\n } else if (isObject(ref)) {\n ref.current = instance;\n } else {\n ownerComponent[INSTANCE].refs[ref] = instance;\n }\n}\n\nexport function detachRef(ownerComponent, ref, component) {\n if (isFunction(ref)) {\n // When the referenced component is unmounted and whenever the ref changes, the old ref will be called with null as an argument.\n ref(null);\n } else {\n // Must match component and ref could detach the ref on owner when A's before ref is B's current ref\n let instance = component.__getPublicInstance();\n\n if (isObject(ref) && ref.current === instance) {\n ref.current = null;\n } else if (ownerComponent[INSTANCE].refs[ref] === instance) {\n delete ownerComponent[INSTANCE].refs[ref];\n }\n }\n}\n","import {isArray, isString, isNumber, isObject, isFalsy} from '../types';\n\nfunction shouldUpdateComponent(prevElement, nextElement) {\n let prevFalsy = isFalsy(prevElement);\n let nextFalsy = isFalsy(nextElement);\n if (prevFalsy || nextFalsy) {\n return prevFalsy === nextFalsy;\n }\n\n if (isArray(prevElement) && isArray(nextElement)) {\n return true;\n }\n\n const isPrevStringOrNumber = isString(prevElement) || isNumber(prevElement);\n if (isPrevStringOrNumber) {\n return isString(nextElement) || isNumber(nextElement);\n } else {\n // prevElement and nextElement could be array, typeof [] is \"object\"\n return (\n isObject(prevElement) &&\n isObject(nextElement) &&\n prevElement.type === nextElement.type &&\n prevElement.key === nextElement.key\n );\n }\n}\n\nexport default shouldUpdateComponent;\n","import { isString } from '../types';\nimport { warning } from '../error';\n\nexport default function getElementKeyName(children, element, index) {\n // `element && element.key` will cause elementKey receive \"\" when element is \"\"\n const elementKey = element ? element.key : void 0;\n const defaultName = '.' + index.toString(36); // Inner child name default format fallback\n\n // Key should must be string type\n if (isString(elementKey)) {\n let keyName = '$' + elementKey;\n // Child keys must be unique.\n let keyUnique = children[keyName] === undefined;\n\n if (process.env.NODE_ENV !== 'production') {\n if (!keyUnique) {\n // Only the first child will be used when encountered two children with the same key\n warning(`Encountered two children with the same key \"${elementKey}\".`);\n }\n }\n\n return keyUnique ? keyName : defaultName;\n } else {\n return defaultName;\n }\n}\n","import Host from './host';\nimport { isArray } from '../types';\nimport { INTERNAL } from '../constant';\n\n/**\n * This function is usually been used to find the closet previous sibling native node of FragmentComponent.\n * FragmentComponent does not have a native node in the DOM tree, so when it is replaced, the new node has no corresponding location to insert.\n * So we need to look forward from the current mount position of the FragmentComponent to the nearest component which have the native node.\n * @param component\n * @return nativeNode\n */\nexport default function getPrevSiblingNativeNode(component) {\n let parent = component;\n while (parent = component.__parentInstance &&\n component.__parentInstance[INTERNAL]) {\n if (parent instanceof Host.__Composite) {\n component = parent;\n continue;\n }\n\n const keys = Object.keys(parent.__renderedChildren);\n // Find previous sibling native node from current mount index\n for (let i = component.__mountIndex - 1; i >= 0; i--) {\n const nativeNode = parent.__renderedChildren[keys[i]].__getNativeNode();\n // Fragment component always return array\n if (isArray(nativeNode)) {\n if (nativeNode.length > 0) {\n // Get the last one\n return nativeNode[nativeNode.length - 1];\n }\n } else {\n // Others maybe native node or empty node\n return nativeNode;\n }\n }\n\n // Find parent over parent\n if (parent instanceof Host.__Fragment) {\n component = parent;\n } else {\n return null;\n }\n }\n}\n","import Host from './host';\nimport { INSTANCE, INTERNAL, NATIVE_NODE } from '../constant';\n\n/**\n * Base Component\n */\nexport default class BaseComponent {\n constructor(element) {\n this.__currentElement = element;\n }\n\n __initComponent(parent, parentInstance, context) {\n this._parent = parent;\n this.__parentInstance = parentInstance;\n this._context = context;\n this._mountID = Host.__mountID++;\n }\n\n __destoryComponent() {\n if (process.env.NODE_ENV !== 'production') {\n Host.reconciler.unmountComponent(this);\n }\n\n this.__currentElement\n = this[NATIVE_NODE]\n = this._parent\n = this.__parentInstance\n = this._context\n = null;\n\n if (this[INSTANCE]) {\n this[INSTANCE] = this[INSTANCE][INTERNAL] = null;\n }\n }\n\n __mountComponent(parent, parentInstance, context, nativeNodeMounter) {\n this.__initComponent(parent, parentInstance, context);\n this.__mountNativeNode(nativeNodeMounter);\n\n if (process.env.NODE_ENV !== 'production') {\n Host.reconciler.mountComponent(this);\n }\n\n const instance = {};\n instance[INTERNAL] = this;\n\n return instance;\n }\n\n unmountComponent(shouldNotRemoveChild) {\n if (this[NATIVE_NODE] && !shouldNotRemoveChild) {\n Host.driver.removeChild(this[NATIVE_NODE], this._parent);\n }\n\n this.__destoryComponent();\n }\n\n __getName() {\n let currentElement = this.__currentElement;\n let type = currentElement && currentElement.type;\n\n return (\n type && type.displayName ||\n type && type.name ||\n type || // Native component's name is type\n currentElement\n );\n }\n\n __mountNativeNode(nativeNodeMounter) {\n let nativeNode = this.__getNativeNode();\n let parent = this._parent;\n\n if (nativeNodeMounter) {\n nativeNodeMounter(nativeNode, parent);\n } else {\n Host.driver.appendChild(nativeNode, parent);\n }\n }\n\n __getNativeNode() {\n return this[NATIVE_NODE] == null\n ? this[NATIVE_NODE] = this.__createNativeNode()\n : this[NATIVE_NODE];\n }\n\n __getPublicInstance() {\n return this.__getNativeNode();\n }\n}\n","export default Object.assign;\n","import Host from './host';\nimport { detachRef, attachRef, updateRef } from './ref';\nimport instantiateComponent from './instantiateComponent';\nimport shouldUpdateComponent from './shouldUpdateComponent';\nimport getElementKeyName from './getElementKeyName';\nimport getPrevSiblingNativeNode from './getPrevSiblingNativeNode';\nimport Instance from './instance';\nimport BaseComponent from './base';\nimport toArray from '../toArray';\nimport { isFunction, isArray, isNull } from '../types';\nimport assign from '../assign';\nimport { INSTANCE, INTERNAL, NATIVE_NODE } from '../constant';\n\nconst STYLE = 'style';\nconst CHILDREN = 'children';\nconst TREE = 'tree';\nconst EVENT_PREFIX_REGEXP = /^on[A-Z]/;\n\n/**\n * Native Component\n */\nexport default class NativeComponent extends BaseComponent {\n __mountComponent(parent, parentInstance, context, nativeNodeMounter) {\n this.__initComponent(parent, parentInstance, context);\n\n const currentElement = this.__currentElement;\n const props = currentElement.props;\n const type = currentElement.type;\n const children = props[CHILDREN];\n const appendType = props.append || TREE; // Default is tree\n\n // Clone a copy for style diff\n this.__prevStyleCopy = assign({}, props[STYLE]);\n\n let instance = {\n type,\n props,\n };\n instance[INTERNAL] = this;\n\n this[INSTANCE] = instance;\n\n if (appendType === TREE) {\n // Should after process children when mount by tree mode\n this.__mountChildren(children, context);\n this.__mountNativeNode(nativeNodeMounter);\n } else {\n // Should before process children when mount by node mode\n this.__mountNativeNode(nativeNodeMounter);\n this.__mountChildren(children, context);\n }\n\n // Ref acttach\n if (currentElement && currentElement.ref) {\n attachRef(currentElement._owner, currentElement.ref, this);\n }\n\n if (process.env.NODE_ENV !== 'production') {\n Host.reconciler.mountComponent(this);\n }\n\n return instance;\n }\n\n __mountChildren(children, context) {\n if (children == null) return children;\n\n const nativeNode = this.__getNativeNode();\n return this.__mountChildrenImpl(nativeNode, toArray(children), context);\n }\n\n __mountChildrenImpl(parent, children, context, nativeNodeMounter) {\n let renderedChildren = this.__renderedChildren = {};\n\n const renderedChildrenImage = [];\n for (let i = 0, l = children.length; i < l; i++) {\n const element = children[i];\n const renderedChild = instantiateComponent(element);\n const name = getElementKeyName(renderedChildren, element, i);\n renderedChildren[name] = renderedChild;\n renderedChild.__mountIndex = i;\n // Mount children\n const mountImage = renderedChild.__mountComponent(\n parent,\n this[INSTANCE],\n context,\n nativeNodeMounter\n );\n renderedChildrenImage.push(mountImage);\n }\n\n return renderedChildrenImage;\n }\n\n __unmountChildren(shouldNotRemoveChild) {\n let renderedChildren = this.__renderedChildren;\n\n if (renderedChildren) {\n for (let name in renderedChildren) {\n let renderedChild = renderedChildren[name];\n renderedChild.unmountComponent(shouldNotRemoveChild);\n }\n this.__renderedChildren = null;\n }\n }\n\n unmountComponent(shouldNotRemoveChild) {\n if (this[NATIVE_NODE]) {\n let ref = this.__currentElement.ref;\n if (ref) {\n detachRef(this.__currentElement._owner, ref, this);\n }\n\n Instance.remove(this[NATIVE_NODE]);\n\n if (!shouldNotRemoveChild) {\n Host.driver.removeChild(this[NATIVE_NODE], this._parent);\n }\n }\n\n this.__unmountChildren(true);\n\n this.__prevStyleCopy = null;\n this.__destoryComponent();\n }\n\n __updateComponent(prevElement, nextElement, prevContext, nextContext) {\n // Replace current element\n this.__currentElement = nextElement;\n\n updateRef(prevElement, nextElement, this);\n\n let prevProps = prevElement.props;\n let nextProps = nextElement.props;\n\n this.__updateProperties(prevProps, nextProps);\n\n // If the prevElement has no child, mount children directly\n if (prevProps[CHILDREN] == null ||\n isArray(prevProps[CHILDREN]) && prevProps[CHILDREN].length === 0) {\n this.__mountChildren(nextProps[CHILDREN], nextContext);\n } else {\n this.__updateChildren(nextProps[CHILDREN], nextContext);\n }\n\n if (process.env.NODE_ENV !== 'production') {\n Host.reconciler.receiveComponent(this);\n }\n }\n\n __updateProperties(prevProps, nextProps) {\n let propKey;\n let styleName;\n let styleUpdates;\n const driver = Host.driver;\n const nativeNode = this.__getNativeNode();\n\n for (propKey in prevProps) {\n // Continue children and null value prop or nextProps has some propKey that do noting\n if (\n propKey === CHILDREN ||\n prevProps[propKey] == null ||\n // Use hasOwnProperty here for avoid propKey name is some with method name in object proptotype\n nextProps.hasOwnProperty(propKey)\n ) {\n continue;\n }\n\n if (propKey === STYLE) {\n // Remove all style\n let lastStyle = this.__prevStyleCopy;\n for (styleName in lastStyle) {\n styleUpdates = styleUpdates || {};\n styleUpdates[styleName] = '';\n }\n this.__prevStyleCopy = null;\n } else if (EVENT_PREFIX_REGEXP.test(propKey)) {\n // Remove event\n const eventListener = prevProps[propKey];\n\n if (isFunction(eventListener)) {\n driver.removeEventListener(\n nativeNode,\n propKey.slice(2).toLowerCase(),\n eventListener\n );\n }\n } else {\n // Remove attribute\n driver.removeAttribute(\n nativeNode,\n propKey,\n prevProps[propKey]\n );\n }\n }\n\n for (propKey in nextProps) {\n let nextProp = nextProps[propKey];\n let prevProp = propKey === STYLE ? this.__prevStyleCopy :\n prevProps != null ? prevProps[propKey] : undefined;\n\n // Continue children or prevProp equal nextProp\n if (\n propKey === CHILDREN ||\n prevProp === nextProp ||\n nextProp == null && prevProp == null\n ) {\n continue;\n }\n\n // Update style\n if (propKey === STYLE) {\n if (nextProp) {\n // Clone property\n nextProp = this.__prevStyleCopy = assign({}, nextProp);\n } else {\n this.__prevStyleCopy = null;\n }\n\n if (prevProp != null) {\n // Unset styles on `prevProp` but not on `nextProp`.\n for (styleName in prevProp) {\n if (!nextProp || !nextProp[styleName] && nextProp[styleName] !== 0) {\n styleUpdates = styleUpdates || {};\n styleUpdates[styleName] = '';\n }\n }\n // Update styles that changed since `prevProp`.\n for (styleName in nextProp) {\n if (prevProp[styleName] !== nextProp[styleName]) {\n styleUpdates = styleUpdates || {};\n styleUpdates[styleName] = nextProp[styleName];\n }\n }\n } else {\n // Assign next prop when prev style is null\n styleUpdates = nextProp;\n }\n } else if (EVENT_PREFIX_REGEXP.test(propKey)) {\n // Update event binding\n let eventName = propKey.slice(2).toLowerCase();\n\n if (isFunction(prevProp)) {\n driver.removeEventListener(nativeNode, eventName, prevProp, nextProps);\n }\n\n if (isFunction(nextProp)) {\n driver.addEventListener(nativeNode, eventName, nextProp, nextProps);\n }\n } else {\n // Update other property\n if (nextProp != null) {\n driver.setAttribute(\n nativeNode,\n propKey,\n nextProp\n );\n } else {\n driver.removeAttribute(\n nativeNode,\n propKey,\n prevProps[propKey]\n );\n }\n\n if (process.env.NODE_ENV !== 'production') {\n Host.measurer && Host.measurer.recordOperation({\n instanceID: this._mountID,\n type: 'update attribute',\n payload: {\n [propKey]: nextProp\n }\n });\n }\n }\n }\n\n if (styleUpdates) {\n if (process.env.NODE_ENV !== 'production') {\n Host.measurer && Host.measurer.recordOperation({\n instanceID: this._mountID,\n type: 'update style',\n payload: styleUpdates\n });\n }\n\n driver.setStyle(nativeNode, styleUpdates);\n }\n }\n\n __updateChildren(nextChildrenElements, context) {\n // prev rendered children\n let prevChildren = this.__renderedChildren;\n let driver = Host.driver;\n\n if (nextChildrenElements == null && prevChildren == null) {\n return;\n }\n\n let nextChildren = {};\n\n if (nextChildrenElements != null) {\n nextChildrenElements = toArray(nextChildrenElements);\n\n // Update next children elements\n for (let index = 0, length = nextChildrenElements.length; index < length; index++) {\n let nextElement = nextChildrenElements[index];\n let name = getElementKeyName(nextChildren, nextElement, index);\n let prevChild = prevChildren && prevChildren[name];\n let prevElement = prevChild && prevChild.__currentElement;\n let prevContext = prevChild && prevChild._context;\n\n // Try to update between the two of some name that has some element type,\n // and move child in next children loop if need\n if (prevChild != null && shouldUpdateComponent(prevElement, nextElement)) {\n if (prevElement !== nextElement || prevContext !== context) {\n // Pass the same context when updating children\n prevChild.__updateComponent(prevElement, nextElement, context,\n context);\n }\n\n nextChildren[name] = prevChild;\n } else {\n // Unmount the prevChild when some name with nextChild but different element type,\n // and move child node in next children loop\n if (prevChild) {\n prevChild.__unmount = true;\n }\n // The child must be instantiated before it's mounted.\n nextChildren[name] = instantiateComponent(nextElement);\n }\n }\n }\n\n let parent = this.__getNativeNode();\n let isFragmentParent = isArray(parent);\n let prevFirstChild = null;\n let prevFirstNativeNode = null;\n let isPrevFirstEmptyFragment = false;\n let shouldUnmountPrevFirstChild = false;\n let lastPlacedNode = null;\n\n // Directly remove all children from component, if nextChildren is empty (null, [], '').\n // `driver.removeChildren` is optional driver protocol.\n let shouldRemoveAllChildren = Boolean(\n driver.removeChildren\n // nextChildElements == null or nextChildElements is empty\n && (isNull(nextChildrenElements) || nextChildrenElements && !nextChildrenElements.length)\n // Fragment parent can not remove parentNode's all child nodes directly.\n && !isFragmentParent\n );\n\n // Unmount children that are no longer present.\n if (prevChildren != null) {\n for (let name in prevChildren) {\n let prevChild = prevChildren[name];\n let shouldUnmount = prevChild.__unmount || !nextChildren[name];\n\n // Store old first child ref for append node ahead and maybe delay remove it\n if (!prevFirstChild) {\n shouldUnmountPrevFirstChild = shouldUnmount;\n prevFirstChild = prevChild;\n prevFirstNativeNode = prevFirstChild.__getNativeNode();\n\n if (isArray(prevFirstNativeNode)) {\n isPrevFirstEmptyFragment = prevFirstNativeNode.length === 0;\n prevFirstNativeNode = prevFirstNativeNode[0];\n }\n } else if (shouldUnmount) {\n prevChild.unmountComponent(shouldRemoveAllChildren);\n }\n }\n\n // 1. When fragment embed fragment updated but prev fragment is empty\n // that need to get the prev sibling native node.\n // like: [ [] ] -> [ [1, 2] ]\n // 2. When prev fragment is empty and update to other type\n // like: [ [], 1 ] -> [ 1, 2 ]\n if (isFragmentParent && parent.length === 0 || isPrevFirstEmptyFragment) {\n lastPlacedNode = getPrevSiblingNativeNode(this);\n }\n }\n\n\n if (nextChildren != null) {\n // `nextIndex` will increment for each child in `nextChildren`\n let nextIndex = 0;\n\n function