rax
Version:
A universal React-compatible render engine.
1,493 lines (1,409 loc) • 91.1 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Rax = {}));
})(this, (function (exports) {
/*
* Stateful things in runtime
*/
var Host = {
__mountID: 1,
__isUpdating: false,
// Inject
driver: null,
// Roots
rootComponents: {},
rootInstances: {},
// Current owner component
owner: null
};
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var ReactPropTypesSecret$1 = 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED';
var ReactPropTypesSecret_1 = ReactPropTypesSecret$1;
var has$1 = Function.call.bind(Object.prototype.hasOwnProperty);
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
var printWarning = function() {};
{
var ReactPropTypesSecret = ReactPropTypesSecret_1;
var loggedTypeFailures = {};
var has = has$1;
printWarning = function(text) {
var message = 'Warning: ' + text;
if (typeof console !== 'undefined') {
console.error(message);
}
try {
// --- Welcome to debugging React ---
// This error was thrown as a convenience so that you can use this stack
// to find the callsite that caused this warning to fire.
throw new Error(message);
} catch (x) { /**/ }
};
}
/**
* Assert that the values match with the type specs.
* Error messages are memorized and will only be shown once.
*
* @param {object} typeSpecs Map of name to a ReactPropType
* @param {object} values Runtime values that need to be type-checked
* @param {string} location e.g. "prop", "context", "child context"
* @param {string} componentName Name of the component for error messages.
* @param {?Function} getStack Returns the component stack.
* @private
*/
function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
{
for (var typeSpecName in typeSpecs) {
if (has(typeSpecs, typeSpecName)) {
var error;
// Prop type validation may throw. In case they do, we don't want to
// fail the render phase where it didn't fail before. So we log it.
// After these have been cleaned up, we'll let them throw.
try {
// This is intentionally an invariant that gets caught. It's the same
// behavior as without this statement except with a better message.
if (typeof typeSpecs[typeSpecName] !== 'function') {
var err = Error(
(componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' +
'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' +
'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.'
);
err.name = 'Invariant Violation';
throw err;
}
error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
} catch (ex) {
error = ex;
}
if (error && !(error instanceof Error)) {
printWarning(
(componentName || 'React class') + ': type specification of ' +
location + ' `' + typeSpecName + '` is invalid; the type checker ' +
'function must return `null` or an `Error` but returned a ' + typeof error + '. ' +
'You may have forgotten to pass an argument to the type checker ' +
'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
'shape all require an argument).'
);
}
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
// Only monitor this failure once because there tends to be a lot of the
// same error.
loggedTypeFailures[error.message] = true;
var stack = getStack ? getStack() : '';
printWarning(
'Failed ' + location + ' type: ' + error.message + (stack != null ? stack : '')
);
}
}
}
}
}
/**
* Resets warning cache when testing.
*
* @private
*/
checkPropTypes.resetWarningCache = function() {
{
loggedTypeFailures = {};
}
};
var checkPropTypes_1 = checkPropTypes;
var checkPropTypes$1 = checkPropTypes_1;
function Element(type, key, ref, props, owner) {
var element = {
// Built-in properties that belong on the element
type: type,
key: key,
ref: ref,
props: props,
// Record the component responsible for creating this element.
_owner: owner
};
{
var propTypes = type.propTypes;
// Validate its props provided by the propTypes definition
if (propTypes) {
var displayName = type.displayName || type.name;
checkPropTypes$1(propTypes, props, 'prop', displayName);
}
// We make validation flag non-enumerable, so the test framework could ignore it
Object.defineProperty(element, '__validated', {
configurable: false,
enumerable: false,
writable: true,
value: false
});
// Props is immutable
if (Object.freeze) {
Object.freeze(props);
}
}
return element;
}
function isNull(obj) {
return obj === null;
}
function isFunction(obj) {
return typeof obj === 'function';
}
function isObject(obj) {
return typeof obj === 'object';
}
function isPlainObject(obj) {
return EMPTY_OBJECT.toString.call(obj) === '[object Object]';
}
function isArray(array) {
return Array.isArray(array);
}
function isString(string) {
return typeof string === 'string';
}
function isNumber(string) {
return typeof string === 'number';
}
function isFalsy(val) {
return !Boolean(val);
}
var NOOP = function NOOP() {};
var EMPTY_OBJECT = {};
function traverseChildren(children, result) {
if (isArray(children)) {
for (var i = 0, l = children.length; i < l; i++) {
traverseChildren(children[i], result);
}
} else {
result.push(children);
}
}
function flattenChildren(children) {
if (children == null) {
return children;
}
var result = [];
traverseChildren(children, result);
// If length equal 1, return the only one.
return result.length - 1 ? result : result[0];
}
var updateCallbacks = [];
var effectCallbacks = [];
var layoutCallbacks = [];
var scheduler = setTimeout;
{
// Wrapper timer for hijack timers in jest
scheduler = function scheduler(callback) {
setTimeout(callback);
};
}
function invokeFunctionsWithClear(callbacks) {
var callback;
while (callback = callbacks.shift()) {
callback();
}
}
// Schedule before next render
function schedule(callback) {
if (updateCallbacks.length === 0) {
scheduler(flush);
}
updateCallbacks.push(callback);
}
// Flush before next render
function flush() {
invokeFunctionsWithClear(updateCallbacks);
}
function scheduleEffect(callback) {
if (effectCallbacks.length === 0) {
scheduler(flushEffect);
}
effectCallbacks.push(callback);
}
function flushEffect() {
invokeFunctionsWithClear(effectCallbacks);
}
function scheduleLayout(callback) {
layoutCallbacks.push(callback);
}
function flushLayout() {
invokeFunctionsWithClear(layoutCallbacks);
}
function createMinifiedError(type, code, obj) {
var typeInfo = obj === undefined ? '' : ' got: ' + getTypeInfo(obj);
return new Error(type + ": #" + code + ", " + getRenderErrorInfo() + "." + typeInfo);
}
function getTypeInfo(obj) {
return isPlainObject(obj) ? Object.keys(obj) : obj;
}
function getRenderErrorInfo() {
var ownerComponent = Host.owner;
return ownerComponent ? "check <" + ownerComponent.__getName() + ">" : 'no owner';
}
/**
* Minified code:
* 1: Hooks called outside a component, or multiple version of Rax are used.
* 6: Invalid component type, expected a class or function component.
* 4: Too many re-renders, the number of renders is limited to prevent an infinite loop.
* 5: Rax driver not found.
* @param code {Number}
* @param obj {Object}
*/
function throwMinifiedError(code, obj) {
throw createMinifiedError('Error', code, obj);
}
function throwError(message, obj) {
var typeInfo = obj === undefined ? '' : '(found: ' + (isPlainObject(obj) ? "object with keys {" + Object.keys(obj) + "}" : obj) + ')';
throw Error(message + " " + typeInfo);
}
var warning = NOOP;
{
warning = function warning(template) {
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
args[_key - 1] = arguments[_key];
}
if (typeof console !== 'undefined') {
var argsWithFormat = args.map(function (item) {
return '' + item;
});
argsWithFormat.unshift('Warning: ' + template);
// Don't use spread (or .apply) directly because it breaks IE9
Function.prototype.apply.call(console.error, console, argsWithFormat);
}
// For works in DevTools when enable `Pause on caught exceptions`
// that can find the component where caused this warning
try {
var argIndex = 0;
var message = 'Warning: ' + template.replace(/%s/g, function () {
return args[argIndex++];
});
throw new Error(message);
} catch (e) {}
};
}
/**
* Warn if there's no key explicitly set on dynamic arrays of children or
* object keys are not valid. This allows us to keep track of children between
* updates.
*/
var ownerHasKeyUseWarning = {};
function getCurrentComponentErrorInfo(parentType) {
var info = '';
var ownerComponent = Host.owner;
if (ownerComponent) {
var name = ownerComponent.__getName();
if (name) {
info = " Check the render method of <" + name + ">.";
}
}
if (!info) {
var parentName = typeof parentType === 'string' ? parentType : parentType.displayName || parentType.name;
if (parentName) {
info = " Check the top-level render call using <" + parentName + ">.";
}
}
return info;
}
function isValidElement(object) {
return typeof object === 'object' && object !== null && object.type && !!object.props;
}
function validateExplicitKey(element, parentType) {
if (element.__validated || element.key != null) {
return;
}
element.__validated = true;
var currentComponentErrorInfo = getCurrentComponentErrorInfo(parentType);
if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
return;
}
ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
// Usually the current owner is the offender, but if it accepts children as a
// property, it may be the creator of the child that's responsible for
// assigning it a key.
var childOwner = '';
if (element && element._owner && element._owner !== Host.owner) {
// Give the component that originally created this child.
childOwner = " It was passed a child from <" + element._owner.__getName() + ">.";
}
warning("Each child in a list should have a unique \"key\" prop." + currentComponentErrorInfo + childOwner);
}
function validateChildKeys(node, parentType) {
// Only array or element object is valid child
if (typeof node !== 'object') {
return;
}
if (isArray(node)) {
for (var i = 0; i < node.length; i++) {
var child = node[i];
if (isValidElement(child)) {
validateExplicitKey(child, parentType);
}
}
} else if (isValidElement(node)) {
node.__validated = true;
}
// Rax don't support iterator object as element children
// TODO: add validate when rax support iterator object as element.
}
var RESERVED_PROPS = {
key: true,
ref: true
};
function createElement(type, config, children) {
// Reserved names are extracted
var props = {};
var propName;
var key = null;
var ref = null;
if (config != null) {
ref = config.ref === undefined ? null : config.ref;
key = config.key === undefined ? null : '' + config.key;
// Remaining properties are added to a new props object
for (propName in config) {
if (!RESERVED_PROPS[propName]) {
props[propName] = config[propName];
}
}
}
// Children arguments can be more than one
var childrenLength = arguments.length - 2;
if (childrenLength > 0) {
if (childrenLength === 1 && !isArray(children)) {
props.children = children;
} else {
var childArray = children;
if (childrenLength > 1) {
childArray = new Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
}
props.children = flattenChildren(childArray);
}
}
// Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
if (type == null) {
{
throwError("Invalid element type, expected a string or a class/function component but got \"" + type + "\".");
}
}
{
if (isString(ref) && !Host.owner) {
warning("Adding a string ref \"" + ref + "\" that was not created inside render method, or multiple copies of Rax are used.");
}
for (var _i = 2; _i < arguments.length; _i++) {
validateChildKeys(arguments[_i], type);
}
}
return new Element(type, key, ref, props, Host.owner);
}
function invokeFunctionsWithContext(fns, context, value) {
for (var i = 0, l = fns && fns.length; i < l; i++) {
fns[i].call(context, value);
}
}
var hasOwnProperty = EMPTY_OBJECT.hasOwnProperty;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
function is(x, y) {
// SameValue algorithm
if (x === y) {
// Steps 1-5, 7-10
// Steps 6.b-6.e: +0 != -0
return x !== 0 || 1 / x === 1 / y;
} else {
// Step 6.a: NaN == NaN
return x !== x && y !== y; // eslint-disable-line no-self-compare
}
}
/**
* Performs equality by iterating through keys on an object and returning false
* when any key has values which are not strictly equal between the arguments.
* Returns true when the values of all keys are strictly equal.
*/
function shallowEqual(objA, objB) {
if (is(objA, objB)) {
return true;
}
if (!isObject(objA) || isNull(objA) || !isObject(objB) || isNull(objB)) {
return false;
}
var keysA = Object.keys(objA);
var keysB = Object.keys(objB);
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (var i = 0; i < keysA.length; i++) {
if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
return false;
}
}
return true;
}
/* Common constant variables for rax */
var INTERNAL = '_internal';
var INSTANCE = '_instance';
var NATIVE_NODE = '_nativeNode';
var RENDERED_COMPONENT = '_renderedComponent';
function getCurrentInstance() {
return Host.owner && Host.owner[INSTANCE];
}
function getCurrentRenderingInstance() {
var currentInstance = getCurrentInstance();
if (currentInstance) {
return currentInstance;
} else {
{
throwError('Hooks called outside a component, or multiple version of Rax are used.');
}
}
}
function areInputsEqual(inputs, prevInputs) {
if (isNull(prevInputs) || inputs.length !== prevInputs.length) {
return false;
}
for (var i = 0; i < inputs.length; i++) {
if (is(inputs[i], prevInputs[i])) {
continue;
}
return false;
}
return true;
}
function useState(initialState) {
var currentInstance = getCurrentRenderingInstance();
var hookID = currentInstance.getHookID();
var hooks = currentInstance.getHooks();
if (!hooks[hookID]) {
// If the initial state is the result of an expensive computation,
// you may provide a function instead for lazy initial state.
if (isFunction(initialState)) {
initialState = initialState();
}
var setState = function setState(newState) {
// Flush all effects first before update state
if (!Host.__isUpdating) {
flushEffect();
}
var hook = hooks[hookID];
var eagerState = hook[2];
// function updater
if (isFunction(newState)) {
newState = newState(eagerState);
}
if (!is(newState, eagerState)) {
// Current instance is in render update phase.
// After this one render finish, will continue run.
hook[2] = newState;
if (getCurrentInstance() === currentInstance) {
// Marked as is scheduled that could finish hooks.
currentInstance.__isScheduled = true;
} else {
currentInstance.__update();
}
}
};
hooks[hookID] = [initialState, setState, initialState];
}
var hook = hooks[hookID];
if (!is(hook[0], hook[2])) {
hook[0] = hook[2];
currentInstance.__shouldUpdate = true;
}
return hook;
}
function useContext(context) {
var currentInstance = getCurrentRenderingInstance();
return currentInstance.useContext(context);
}
function useEffect(effect, inputs) {
useEffectImpl(effect, inputs, true);
}
function useLayoutEffect(effect, inputs) {
useEffectImpl(effect, inputs);
}
function useEffectImpl(effect, inputs, defered) {
var currentInstance = getCurrentRenderingInstance();
var hookID = currentInstance.getHookID();
var hooks = currentInstance.getHooks();
inputs = inputs === undefined ? null : inputs;
if (!hooks[hookID]) {
var __create = function __create(immediately) {
if (!immediately && defered) return scheduleEffect(function () {
return __create(true);
});
var current = __create.current;
if (current) {
__destory.current = current();
__create.current = null;
{
var currentDestory = __destory.current;
if (currentDestory !== undefined && typeof currentDestory !== 'function') {
var msg;
if (currentDestory === null) {
msg = ' You returned null. If your effect does not require clean ' + 'up, return undefined (or nothing).';
} else if (typeof currentDestory.then === 'function') {
msg = '\n\nIt looks like you wrote useEffect(async () => ...) or returned a Promise. ' + 'Instead, write the async function inside your effect ' + 'and call it immediately:\n\n' + 'useEffect(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + '}, [someId]); // Or [] if effect doesn\'t need props or state.';
} else {
msg = ' You returned: ' + currentDestory;
}
warning('An effect function must not return anything besides a function, ' + 'which is used for clean-up.' + msg);
}
}
}
};
var __destory = function __destory(immediately) {
if (!immediately && defered) return scheduleEffect(function () {
return __destory(true);
});
var current = __destory.current;
if (current) {
current();
__destory.current = null;
}
};
__create.current = effect;
hooks[hookID] = {
__create: __create,
__destory: __destory,
__prevInputs: inputs,
__inputs: inputs
};
currentInstance.didMount.push(__create);
currentInstance.willUnmount.push(function () {
return __destory(true);
});
currentInstance.didUpdate.push(function () {
var _hooks$hookID = hooks[hookID],
__prevInputs = _hooks$hookID.__prevInputs,
__inputs = _hooks$hookID.__inputs,
__create = _hooks$hookID.__create;
if (__inputs == null || !areInputsEqual(__inputs, __prevInputs)) {
__destory();
__create();
}
});
} else {
var hook = hooks[hookID];
var _create = hook.__create,
prevInputs = hook.__inputs;
hook.__inputs = inputs;
hook.__prevInputs = prevInputs;
_create.current = effect;
}
}
function useImperativeHandle(ref, create, inputs) {
var nextInputs = isArray(inputs) ? inputs.concat([ref]) : null;
useLayoutEffect(function () {
if (isFunction(ref)) {
ref(create());
return function () {
return ref(null);
};
} else if (ref != null) {
ref.current = create();
return function () {
ref.current = null;
};
}
}, nextInputs);
}
function useRef(initialValue) {
var currentInstance = getCurrentRenderingInstance();
var hookID = currentInstance.getHookID();
var hooks = currentInstance.getHooks();
if (!hooks[hookID]) {
hooks[hookID] = {
current: initialValue
};
}
return hooks[hookID];
}
function useCallback(callback, inputs) {
return useMemo(function () {
return callback;
}, inputs);
}
function useMemo(create, inputs) {
var currentInstance = getCurrentRenderingInstance();
var hookID = currentInstance.getHookID();
var hooks = currentInstance.getHooks();
inputs = inputs === undefined ? null : inputs;
if (!hooks[hookID]) {
hooks[hookID] = [create(), inputs];
} else {
var prevInputs = hooks[hookID][1];
if (isNull(inputs) || !areInputsEqual(inputs, prevInputs)) {
hooks[hookID] = [create(), inputs];
}
}
return hooks[hookID][0];
}
function useReducer(reducer, initialArg, init) {
var currentInstance = getCurrentRenderingInstance();
var hookID = currentInstance.getHookID();
var hooks = currentInstance.getHooks();
var hook = hooks[hookID];
if (!hook) {
var initialState = isFunction(init) ? init(initialArg) : initialArg;
var dispatch = function dispatch(action) {
// Flush all effects first before update state
if (!Host.__isUpdating) {
flushEffect();
}
var hook = hooks[hookID];
// Reducer will update in the next render, before that we add all
// actions to the queue
var queue = hook[2];
if (getCurrentInstance() === currentInstance) {
queue.__actions.push(action);
currentInstance.__isScheduled = true;
} else {
var currentState = queue.__eagerState;
var eagerReducer = queue.__eagerReducer;
var eagerState = eagerReducer(currentState, action);
if (is(eagerState, currentState)) {
return;
}
queue.__eagerState = eagerState;
queue.__actions.push(action);
currentInstance.__update();
}
};
return hooks[hookID] = [initialState, dispatch, {
__actions: [],
__eagerReducer: reducer,
__eagerState: initialState
}];
}
var queue = hook[2];
var next = hook[0];
if (currentInstance.__reRenders > 0) {
for (var i = 0; i < queue.__actions.length; i++) {
next = reducer(next, queue.__actions[i]);
}
} else {
next = queue.__eagerState;
}
if (!is(next, hook[0])) {
hook[0] = next;
currentInstance.__shouldUpdate = true;
}
queue.__eagerReducer = reducer;
queue.__eagerState = next;
queue.__actions.length = 0;
return hooks[hookID];
}
function toArray(obj) {
return isArray(obj) ? obj : [obj];
}
function getNearestParent(instance, matcher) {
var parent;
while (instance && instance[INTERNAL]) {
if (matcher(instance)) {
parent = instance;
break;
}
instance = instance[INTERNAL].__parentInstance;
}
return parent;
}
var id = 0;
function createContext(defaultValue) {
var contextID = '_c' + id++;
// Provider Component
var Provider = /*#__PURE__*/function () {
function Provider() {
this.__contextID = contextID;
this.__handlers = [];
}
var _proto = Provider.prototype;
_proto.__on = function __on(handler) {
this.__handlers.push(handler);
};
_proto.__off = function __off(handler) {
this.__handlers = this.__handlers.filter(function (h) {
return h !== handler;
});
}
// Like getChildContext but called in SSR
;
_proto._getChildContext = function _getChildContext() {
var _ref;
return _ref = {}, _ref[contextID] = this, _ref;
}
// `getValue()` called in rax-server-renderer
;
_proto.getValue = function getValue() {
return this.props.value !== undefined ? this.props.value : defaultValue;
};
_proto.componentDidUpdate = function componentDidUpdate(prevProps) {
if (this.props.value !== prevProps.value) {
invokeFunctionsWithContext(this.__handlers, null, this.getValue());
}
};
_proto.render = function render() {
return this.props.children;
};
return Provider;
}();
function getNearestParentProvider(instance) {
return getNearestParent(instance, function (parent) {
return parent.__contextID === contextID;
});
}
// Consumer Component
function Consumer(props, context) {
var _this = this;
// Current `context[contextID]` only works in SSR
var _useState = useState(function () {
return context[contextID] || getNearestParentProvider(_this);
}),
provider = _useState[0];
var value = provider ? provider.getValue() : defaultValue;
var _useState2 = useState(value),
prevValue = _useState2[0],
setValue = _useState2[1];
if (value !== prevValue) {
setValue(value);
return; // Interrupt execution of consumer.
}
useLayoutEffect(function () {
if (provider) {
provider.__on(setValue);
return function () {
provider.__off(setValue);
};
}
}, []);
// Consumer requires a function as a child.
// The function receives the current context value.
var consumer = toArray(props.children)[0];
if (isFunction(consumer)) {
return consumer(value);
}
}
return {
Provider: Provider,
Consumer: Consumer,
// `_contextID` and `_defaultValue` accessed in rax-server-renderer
_contextID: contextID,
_defaultValue: defaultValue,
__getNearestParentProvider: getNearestParentProvider
};
}
function createRef() {
return {
current: null
};
}
function forwardRef (render) {
// _forwardRef is also use in rax server renderer
render._forwardRef = true;
return render;
}
function memo(type, compare) {
compare = compare || shallowEqual;
// Memo could composed
if (type.__compares) {
type.__compares.push(compare);
} else {
type.__compares = [compare];
}
return type;
}
function Fragment(props) {
return props.children;
}
function injectRenderOptions(_ref) {
var driver = _ref.driver,
measurer = _ref.measurer;
// Inject render driver
if (!(Host.driver = driver || Host.driver)) {
{
throwError('Rax driver not found.');
}
}
{
// Inject performance measurer
Host.measurer = measurer;
}
}
function instantiateComponent(element) {
var instance;
if (isPlainObject(element) && element !== null && element.type) {
// Special case string values
if (isString(element.type)) {
instance = new Host.__Native(element);
} else {
instance = new Host.__Composite(element);
}
} else if (isString(element) || isNumber(element)) {
instance = new Host.__Text(String(element));
} else if (isArray(element)) {
instance = new Host.__Fragment(element);
} else {
if (!(element === undefined || isNull(element) || element === false || element === true)) {
{
throwError('Invalid child type, expected types: Element instance, string, boolean, array, null, undefined.', element);
}
}
instance = new Host.__Empty(element);
}
return instance;
}
function _inheritsLoose(subClass, superClass) {
subClass.prototype = Object.create(superClass.prototype);
subClass.prototype.constructor = subClass;
_setPrototypeOf(subClass, superClass);
}
function _setPrototypeOf(o, p) {
_setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) {
o.__proto__ = p;
return o;
};
return _setPrototypeOf(o, p);
}
/**
* Base component class.
*/
var Component = /*#__PURE__*/function () {
function Component(props, context) {
this.props = props;
this.context = context;
this.refs = {};
}
var _proto = Component.prototype;
_proto.setState = function setState(partialState, callback) {
// The updater property is injected when composite component mounting
this.updater.setState(this, partialState, callback);
};
_proto.forceUpdate = function forceUpdate(callback) {
this.updater.forceUpdate(this, callback);
};
return Component;
}();
var PureComponent = /*#__PURE__*/function (_Component) {
_inheritsLoose(PureComponent, _Component);
function PureComponent(props, context) {
var _this;
_this = _Component.call(this, props, context) || this;
_this.__isPureComponent = true;
return _this;
}
return PureComponent;
}(Component);
var rootID = 1;
var Root = /*#__PURE__*/function (_Component) {
_inheritsLoose(Root, _Component);
function Root() {
var _this;
_this = _Component.call(this) || this;
// Using fragment instead of null for avoid create a comment node when init mount
_this.__element = [];
_this.__rootID = rootID++;
return _this;
}
var _proto = Root.prototype;
_proto.__getPublicInstance = function __getPublicInstance() {
return this.__getRenderedComponent().__getPublicInstance();
};
_proto.__getRenderedComponent = function __getRenderedComponent() {
return this[INTERNAL][RENDERED_COMPONENT];
};
_proto.__update = function __update(element) {
this.__element = element;
this.forceUpdate();
};
_proto.render = function render() {
return this.__element;
};
return Root;
}(Component);
/**
* Instance manager
* @NOTE Key should not be compressed, for that will be added to native node and cause DOM Exception.
*/
var KEY = '_r';
var Instance = {
set: function set(node, instance) {
if (!node[KEY]) {
node[KEY] = instance;
// Record root instance to roots map
if (instance.__rootID) {
Host.rootInstances[instance.__rootID] = instance;
Host.rootComponents[instance.__rootID] = instance[INTERNAL];
}
}
},
get: function get(node) {
return node[KEY];
},
remove: function remove(node) {
var instance = this.get(node);
if (instance) {
node[KEY] = null;
if (instance.__rootID) {
delete Host.rootComponents[instance.__rootID];
delete Host.rootInstances[instance.__rootID];
}
}
},
mount: function mount(element, container, _ref) {
var parent = _ref.parent,
hydrate = _ref.hydrate;
{
Host.measurer && Host.measurer.beforeRender();
}
var driver = Host.driver;
// Real native root node is body
if (container == null) {
container = driver.createBody();
}
var renderOptions = {
element: element,
container: container,
hydrate: hydrate
};
// Before render callback
driver.beforeRender && driver.beforeRender(renderOptions);
// Get the context from the conceptual parent component.
var parentContext;
if (parent) {
var parentInternal = parent[INTERNAL];
parentContext = parentInternal.__processChildContext(parentInternal._context);
}
// Update root component
var prevRootInstance = this.get(container);
if (prevRootInstance && prevRootInstance.__rootID) {
if (parentContext) {
// Using __penddingContext to pass new context
prevRootInstance[INTERNAL].__penddingContext = parentContext;
}
prevRootInstance.__update(element);
// After render callback
driver.afterRender && driver.afterRender(renderOptions);
return prevRootInstance;
}
// Init root component with empty children
var renderedComponent = instantiateComponent(createElement(Root));
var defaultContext = parentContext || {};
var rootInstance = renderedComponent.__mountComponent(container, parent, defaultContext);
this.set(container, rootInstance);
// Mount new element through update queue avoid when there is in rendering phase
rootInstance.__update(element);
// After render callback
driver.afterRender && driver.afterRender(renderOptions);
{
// Devtool render new root hook
Host.reconciler.renderNewRootComponent(rootInstance[INTERNAL][RENDERED_COMPONENT]);
Host.measurer && Host.measurer.afterRender();
}
return rootInstance;
}
};
/*
* Ref manager
*/
function updateRef(prevElement, nextElement, component) {
var prevRef = prevElement ? prevElement.ref : null;
var nextRef = nextElement ? nextElement.ref : null;
// Update refs in owner component
if (prevRef !== nextRef) {
// Detach prev RenderedElement's ref
prevRef && detachRef(prevElement._owner, prevRef, component);
// Attach next RenderedElement's ref
nextRef && attachRef(nextElement._owner, nextRef, component);
}
}
function attachRef(ownerComponent, ref, component) {
if (!ownerComponent) {
{
warning('Ref can not attach because multiple copies of Rax are used.');
}
return;
}
var instance = component.__getPublicInstance();
{
if (instance == null) {
warning('Do not attach ref to function component because they don’t have instances.');
}
}
if (isFunction(ref)) {
ref(instance);
} else if (isObject(ref)) {
ref.current = instance;
} else {
ownerComponent[INSTANCE].refs[ref] = instance;
}
}
function detachRef(ownerComponent, ref, component) {
if (isFunction(ref)) {
// When the referenced component is unmounted and whenever the ref changes, the old ref will be called with null as an argument.
ref(null);
} else {
// Must match component and ref could detach the ref on owner when A's before ref is B's current ref
var instance = component.__getPublicInstance();
if (isObject(ref) && ref.current === instance) {
ref.current = null;
} else if (ownerComponent[INSTANCE].refs[ref] === instance) {
delete ownerComponent[INSTANCE].refs[ref];
}
}
}
function shouldUpdateComponent(prevElement, nextElement) {
var prevFalsy = isFalsy(prevElement);
var nextFalsy = isFalsy(nextElement);
if (prevFalsy || nextFalsy) {
return prevFalsy === nextFalsy;
}
if (isArray(prevElement) && isArray(nextElement)) {
return true;
}
var isPrevStringOrNumber = isString(prevElement) || isNumber(prevElement);
if (isPrevStringOrNumber) {
return isString(nextElement) || isNumber(nextElement);
} else {
// prevElement and nextElement could be array, typeof [] is "object"
return isObject(prevElement) && isObject(nextElement) && prevElement.type === nextElement.type && prevElement.key === nextElement.key;
}
}
function getElementKeyName(children, element, index) {
// `element && element.key` will cause elementKey receive "" when element is ""
var elementKey = element ? element.key : void 0;
var defaultName = '.' + index.toString(36); // Inner child name default format fallback
// Key should must be string type
if (isString(elementKey)) {
var keyName = '$' + elementKey;
// Child keys must be unique.
var keyUnique = children[keyName] === undefined;
{
if (!keyUnique) {
// Only the first child will be used when encountered two children with the same key
warning("Encountered two children with the same key \"" + elementKey + "\".");
}
}
return keyUnique ? keyName : defaultName;
} else {
return defaultName;
}
}
/**
* This function is usually been used to find the closet previous sibling native node of FragmentComponent.
* 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.
* So we need to look forward from the current mount position of the FragmentComponent to the nearest component which have the native node.
* @param component
* @return nativeNode
*/
function getPrevSiblingNativeNode(component) {
var parent = component;
while (parent = component.__parentInstance && component.__parentInstance[INTERNAL]) {
if (parent instanceof Host.__Composite) {
component = parent;
continue;
}
var keys = Object.keys(parent.__renderedChildren);
// Find previous sibling native node from current mount index
for (var i = component.__mountIndex - 1; i >= 0; i--) {
var nativeNode = parent.__renderedChildren[keys[i]].__getNativeNode();
// Fragment component always return array
if (isArray(nativeNode)) {
if (nativeNode.length > 0) {
// Get the last one
return nativeNode[nativeNode.length - 1];
}
} else {
// Others maybe native node or empty node
return nativeNode;
}
}
// Find parent over parent
if (parent instanceof Host.__Fragment) {
component = parent;
} else {
return null;
}
}
}
/**
* Base Component
*/
var BaseComponent = /*#__PURE__*/function () {
function BaseComponent(element) {
this.__currentElement = element;
}
var _proto = BaseComponent.prototype;
_proto.__initComponent = function __initComponent(parent, parentInstance, context) {
this._parent = parent;
this.__parentInstance = parentInstance;
this._context = context;
this._mountID = Host.__mountID++;
};
_proto.__destoryComponent = function __destoryComponent() {
{
Host.reconciler.unmountComponent(this);
}
this.__currentElement = this[NATIVE_NODE] = this._parent = this.__parentInstance = this._context = null;
if (this[INSTANCE]) {
this[INSTANCE] = this[INSTANCE][INTERNAL] = null;
}
};
_proto.__mountComponent = function __mountComponent(parent, parentInstance, context, nativeNodeMounter) {
this.__initComponent(parent, parentInstance, context);
this.__mountNativeNode(nativeNodeMounter);
{
Host.reconciler.mountComponent(this);
}
var instance = {};
instance[INTERNAL] = this;
return instance;
};
_proto.unmountComponent = function unmountComponent(shouldNotRemoveChild) {
if (this[NATIVE_NODE] && !shouldNotRemoveChild) {
Host.driver.removeChild(this[NATIVE_NODE], this._parent);
}
this.__destoryComponent();
};
_proto.__getName = function __getName() {
var currentElement = this.__currentElement;
var type = currentElement && currentElement.type;
return type && type.displayName || type && type.name || type ||
// Native component's name is type
currentElement;
};
_proto.__mountNativeNode = function __mountNativeNode(nativeNodeMounter) {
var nativeNode = this.__getNativeNode();
var parent = this._parent;
if (nativeNodeMounter) {
nativeNodeMounter(nativeNode, parent);
} else {
Host.driver.appendChild(nativeNode, parent);
}
};
_proto.__getNativeNode = function __getNativeNode() {
return this[NATIVE_NODE] == null ? this[NATIVE_NODE] = this.__createNativeNode() : this[NATIVE_NODE];
};
_proto.__getPublicInstance = function __getPublicInstance() {
return this.__getNativeNode();
};
return BaseComponent;
}();
var assign = Object.assign;
var STYLE = 'style';
var CHILDREN = 'children';
var TREE = 'tree';
var EVENT_PREFIX_REGEXP = /^on[A-Z]/;
/**
* Native Component
*/
var NativeComponent = /*#__PURE__*/function (_BaseComponent) {
_inheritsLoose(NativeComponent, _BaseComponent);
function NativeComponent() {
return _BaseComponent.apply(this, arguments) || this;
}
var _proto = NativeComponent.prototype;
_proto.__mountComponent = function __mountComponent(parent, parentInstance, context, nativeNodeMounter) {
this.__initComponent(parent, parentInstance, context);
var currentElement = this.__currentElement;
var props = currentElement.props;
var type = currentElement.type;
var children = props[CHILDREN];
var appendType = props.append || TREE; // Default is tree
// Clone a copy for style diff
this.__prevStyleCopy = assign({}, props[STYLE]);
var instance = {
type: type,
props: props
};
instance[INTERNAL] = this;
this[INSTANCE] = instance;
if (appendType === TREE) {
// Should after process children when mount by tree mode
this.__mountChildren(children, context);
this.__mountNativeNode(nativeNodeMounter);
} else {
// Should before process children when mount by node mode
this.__mountNativeNode(nativeNodeMounter);
this.__mountChildren(children, context);
}
// Ref acttach
if (currentElement && currentElement.ref) {
attachRef(currentElement._owner, currentElement.ref, this);
}
{
Host.reconciler.mountComponent(this);
}
return instance;
};
_proto.__mountChildren = function __mountChildren(children, context) {
if (children == null) return children;
var nativeNode = this.__getNativeNode();
return this.__mountChildrenImpl(nativeNode, toArray(children), context);
};
_proto.__mountChildrenImpl = function __mountChildrenImpl(parent, children, context, nativeNodeMounter) {
var renderedChildren = this.__renderedChildren = {};
var renderedChildrenImage = [];
for (var i = 0, l = children.length; i < l; i++) {
var element = children[i];
var renderedChild = instantiateComponent(element);
var name = getElementKeyName(renderedChildren, element, i);
renderedChildren[name] = renderedChild;
renderedChild.__mountIndex = i;
// Mount children
var mountImage = renderedChild.__mountComponent(parent, this[INSTANCE], context, nativeNodeMounter);
renderedChildrenImage.push(mountImage);
}
return renderedChildrenImage;
};
_proto.__unmountChildren = function __unmountChildren(shouldNotRemoveChild) {
var renderedChildren = this.__renderedChildren;
if (renderedChildren) {
for (var name in renderedChildren) {
var renderedChild = renderedChildren[name];
renderedChild.unmountComponent(shouldNotRemoveChild);
}
this.__renderedChildren = null;
}
};
_proto.unmountComponent = function unmountComponent(shouldNotRemoveChild) {
if (this[NATIVE_NODE]) {
var ref = this.__currentElement.ref;
if (ref) {
detachRef(this.__currentElement._owner, ref, this);
}
Instance.remove(this[NATIVE_NODE]);
if (!shouldNotRemoveChild) {
Host.driver.removeChild(this[NATIVE_NODE], this._parent);
}
}
this.__unmountChildren(true);
this.__prevStyleCopy = null;
this.__destoryComponent();
};
_proto.__updateComponent = function __updateComponent(prevElement, nextElement, prevContext, nextContext) {
// Replace current element
this.__currentElement = nextElement;
updateRef(prevElement, nextElement, this);
var prevProps = prevElement.props;
var nextProps = nextElement.props;
this.__updateProperties(prevProps, nextProps);
// If the prevElement has no child, mount children directly
if (prevProps[CHILDREN] == null || isArray(prevProps[CHILDREN]) && prevProps[CHILDREN].length === 0) {
this.__mountChildren(nextProps[CHILDREN], nextContext);
} else {
this.__updateChildren(nextProps[CHILDREN], nextContext);
}
{
Host.reconciler.receiveComponent(this);
}
};
_proto.__updateProperties = function __updateProperties(prevProps, nextProps) {
var propKey;
var styleName;
var styleUpdates;
var driver = Host.driver;
var nativeNode = this.__getNativeNode();
for (propKey in prevProps) {
// Continue children and null value prop or nextProps has some propKey that do noting
if (propKey === CHILDREN || prevProps[propKey] == null ||
// Use hasOwnProperty here for avoid propKey name is some with method name in object proptotype
nextProps.hasOwnProperty(propKey)) {
continue;
}
if (propKey === STYLE) {
// Remove all style
var lastStyle = this.__prevStyleCopy;
for (styleName in lastStyle) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
this.__prevStyleCopy = null;
} else if (EVENT_PREFIX_REGEXP.test(propKey)) {
// Remove event
var eventListener = prevProps[propKey];
if (isFunction(eventListener)) {
driver.removeEventListener(nativeNode, propKey.slice(2).toLowerCase(), eventListener);
}
} else {
// Remove attribute
driver.removeAttribute(nativeNode, propKey, prevProps[propKey]);
}
}
for (propKey in nextProps) {
var nextProp = nextProps[propKey];
var prevProp = propKey === STYLE ? this.__prevStyleCopy : prevProps != null ? prevProps[propKey] : undefined;
// Continue children or prevProp equal nextProp
if (propKey === CHILDREN || prevProp === nextProp || nextProp == null && prevProp == null) {
continue;
}
// Update style
if (propKey === STYLE) {
if (nextProp) {
// Clone property
nextProp = this.__prevStyleCopy = assign({}, nextProp);
} else {
this.__prevStyleCopy = null;
}
if (prevProp != null) {
// Unset styles on `prevProp` but not on `nextProp`.
for (styleName in prevProp) {
if (!nextProp || !nextProp[styleName] && nextProp[styleName] !== 0) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = '';
}
}
// Update styles that changed since `prevProp`.
for (styleName in nextProp) {
if (prevProp[styleName] !== nextProp[styleName]) {
styleUpdates = styleUpdates || {};
styleUpdates[styleName] = nextProp[styleName];
}
}
} else {
// Assign next prop when prev style is null
styleUpdates = nextProp;
}
} else if (EVENT_PREFIX_REGEXP.test(propKey)) {
// Update event binding
var eventName = propKey.slice(2).toLowerCase();
if (isFunction(prevProp)) {
driver.removeEventListener(nativeNode, eventName, prevProp, nextProps);
}
if (isFunction(nextProp)) {
driver.addEventListener(nativeNode, eventName, nextProp, nextProps);
}
} else {
// Update other property
if (nextProp != null) {
driver.setAttribute(nativeNode, propKey, nextProp);
} else {
driver.removeAttribute(nativeNode, propKey, prevProps[propKey]);
}
{
var _payload;
Host.measurer && Host.measurer.recordOperation({
instanceID: this._mountID,
type: 'update attribute',
payload: (_payload = {}, _payload[propKey] = nextProp, _payload)
});
}
}