Use invariant in react/utils

Just some therapeutic cleanup.
This commit is contained in:
Tim Yung
2013-07-24 17:19:54 -07:00
committed by Paul O’Shannessy
parent 8d48610b7e
commit bdf2a9bb12
10 changed files with 147 additions and 221 deletions
-2
View File
@@ -85,8 +85,6 @@ function OrderedMapImpl(normalizedObj, computedLength) {
* Validates a "public" key - that is, one that the public facing API supplies.
* The key is then normalized for internal storage. In order to be considered
* valid, all keys must be non-empty, defined, non-null strings or numbers.
* Since this already costs a function invocation, will avoid additional call to
* `throwIf`.
*
* @param {string?} key Validates that the key is suitable for use in a
* `OrderedMap`.
+37 -38
View File
@@ -18,23 +18,7 @@
"use strict";
var throwIf = require('throwIf');
var DUAL_TRANSACTION = 'DUAL_TRANSACTION';
var MISSING_TRANSACTION = 'MISSING_TRANSACTION';
if (__DEV__) {
DUAL_TRANSACTION =
'Cannot initialize transaction when there is already an outstanding ' +
'transaction. Common causes of this are trying to render a component ' +
'when you are already rendering a component or attempting a state ' +
'transition while in a render function. Another possibility is that ' +
'you are rendering new content (or state transitioning) in a ' +
'componentDidRender callback. If this is not the case, please report the ' +
'issue immediately.';
MISSING_TRANSACTION =
'Cannot close transaction when there is none open.';
}
var invariant = require('invariant');
/**
* `Transaction` creates a black box that is able to wrap any method such that
@@ -156,26 +140,33 @@ var Mixin = {
* @return Return value from `method`.
*/
perform: function(method, scope, a, b, c, d, e, f) {
throwIf(this.isInTransaction(), DUAL_TRANSACTION);
invariant(
!this.isInTransaction(),
'Transaction.perform(...): Cannot initialize a transaction when there ' +
'is already an outstanding transaction.'
);
var memberStart = Date.now();
var err = null;
var errorToThrow = null;
var ret;
try {
this.initializeAll();
ret = method.call(scope, a, b, c, d, e, f);
} catch (ie_requires_catch) {
err = ie_requires_catch;
} catch (error) {
// IE8 requires `catch` in order to use `finally`.
errorToThrow = error;
} finally {
var memberEnd = Date.now();
this.methodInvocationTime += (memberEnd - memberStart);
try {
this.closeAll();
} catch (closeAllErr) {
err = err || closeAllErr;
} catch (closeError) {
// If `method` throws, prefer to show that stack trace over any thrown
// by invoking `closeAll`.
errorToThrow = errorToThrow || closeError;
}
}
if (err) {
throw err;
if (errorToThrow) {
throw errorToThrow;
}
return ret;
},
@@ -184,15 +175,17 @@ var Mixin = {
this._isInTransaction = true;
var transactionWrappers = this.transactionWrappers;
var wrapperInitTimes = this.timingMetrics.wrapperInitTimes;
var err = null;
var errorToThrow = null;
for (var i = 0; i < transactionWrappers.length; i++) {
var initStart = Date.now();
var wrapper = transactionWrappers[i];
try {
this.wrapperInitData[i] =
wrapper.initialize ? wrapper.initialize.call(this) : null;
} catch (initErr) {
err = err || initErr; // Remember the first error.
this.wrapperInitData[i] = wrapper.initialize ?
wrapper.initialize.call(this) :
null;
} catch (initError) {
// Prefer to show the stack trace of the first error.
errorToThrow = errorToThrow || initError;
this.wrapperInitData[i] = Transaction.OBSERVED_ERROR;
} finally {
var curInitTime = wrapperInitTimes[i];
@@ -200,8 +193,8 @@ var Mixin = {
wrapperInitTimes[i] = (curInitTime || 0) + (initEnd - initStart);
}
}
if (err) {
throw err;
if (errorToThrow) {
throw errorToThrow;
}
},
@@ -212,10 +205,13 @@ var Mixin = {
* invoked).
*/
closeAll: function() {
throwIf(!this.isInTransaction(), MISSING_TRANSACTION);
invariant(
this.isInTransaction(),
'Transaction.closeAll(): Cannot close transaction when none are open.'
);
var transactionWrappers = this.transactionWrappers;
var wrapperCloseTimes = this.timingMetrics.wrapperCloseTimes;
var err = null;
var errorToThrow = null;
for (var i = 0; i < transactionWrappers.length; i++) {
var wrapper = transactionWrappers[i];
var closeStart = Date.now();
@@ -224,8 +220,9 @@ var Mixin = {
if (initData !== Transaction.OBSERVED_ERROR) {
wrapper.close && wrapper.close.call(this, initData);
}
} catch (closeErr) {
err = err || closeErr; // Remember the first error.
} catch (closeError) {
// Prefer to show the stack trace of the first error.
errorToThrow = errorToThrow || closeError;
} finally {
var closeEnd = Date.now();
var curCloseTime = wrapperCloseTimes[i];
@@ -234,14 +231,16 @@ var Mixin = {
}
this.wrapperInitData.length = 0;
this._isInTransaction = false;
if (err) {
throw err;
if (errorToThrow) {
throw errorToThrow;
}
}
};
var Transaction = {
Mixin: Mixin,
/**
* Token to look for to determine if an error occured.
*/
+22 -38
View File
@@ -18,50 +18,34 @@
"use strict";
var throwIf = require('throwIf');
var INVALID_ARGS = 'INVALID_ACCUM_ARGS';
if (__DEV__) {
INVALID_ARGS =
'accumulate requires non empty (non-null, defined) next ' +
'values. All arrays accumulated must not contain any empty items.';
}
var invariant = require('invariant');
/**
* Accumulates items that must never be empty, into a result in a manner that
* conserves memory - avoiding allocation of arrays until they are needed. The
* accumulation may start and/or end up being a single element or an array
* depending on the total count (if greater than one, an array is allocated).
* Handles most common case first (starting with an empty current value and
* acquiring one).
* @return {Accumulation} An accumulation which is either a single item or an
* Array of items.
* Accumulates items that must not be null or undefined.
*
* This is used to conserve memory by avoiding array allocations.
*
* @return {*|array<*>} An accumulation of items.
*/
function accumulate(cur, next) {
var curValIsEmpty = cur == null; // Will test for emptiness (null/undef)
var nextValIsEmpty = next === null;
if (__DEV__) {
throwIf(nextValIsEmpty, INVALID_ARGS);
}
if (nextValIsEmpty) {
return cur;
function accumulate(current, next) {
invariant(
next != null,
'accumulate(...): Accumulated items must be not be null or undefined.'
);
if (current == null) {
return next;
} else {
if (curValIsEmpty) {
return next;
// Both are not empty. Warning: Never call x.concat(y) when you are not
// certain that x is an Array (x could be a string with concat method).
var currentIsArray = Array.isArray(current);
var nextIsArray = Array.isArray(next);
if (currentIsArray) {
return current.concat(next);
} else {
// Both are not empty. Warning: Never call x.concat(y) when you are not
// certain that x is an Array (x could be a string with concat method).
var curIsArray = Array.isArray(cur);
var nextIsArray = Array.isArray(next);
if (curIsArray) {
return cur.concat(next);
if (nextIsArray) {
return [current].concat(next);
} else {
if (nextIsArray) {
return [cur].concat(next);
} else {
return [cur, next];
}
return [current, next];
}
}
}
+15 -16
View File
@@ -14,19 +14,12 @@
* limitations under the License.
*
* @providesModule escapeTextForBrowser
* @typechecks static-only
*/
"use strict";
var throwIf = require('throwIf');
var ESCAPE_TYPE_ERR;
if (__DEV__) {
ESCAPE_TYPE_ERR =
'The React core has attempted to escape content that is of a ' +
'mysterious type (object etc) Escaping only works on numbers and strings';
}
var invariant = require('invariant');
var ESCAPE_LOOKUP = {
"&": "&amp;",
@@ -41,13 +34,19 @@ function escaper(match) {
return ESCAPE_LOOKUP[match];
}
var escapeTextForBrowser = function (text) {
/**
* Escapes text to prevent scripting attacks.
*
* @param {number|string} text Text value to escape.
* @return {string} An escaped string.
*/
function escapeTextForBrowser(text) {
var type = typeof text;
var invalid = type === 'object';
if (__DEV__) {
throwIf(invalid, ESCAPE_TYPE_ERR);
}
if (text === '' || invalid) {
invariant(
type !== 'object',
'escapeTextForBrowser(...): Attempted to escape an object.'
);
if (text === '') {
return '';
} else {
if (type === 'string') {
@@ -56,6 +55,6 @@ var escapeTextForBrowser = function (text) {
return (''+text).replace(/[&><"'\/]/g, escaper);
}
}
};
}
module.exports = escapeTextForBrowser;
+9 -9
View File
@@ -18,7 +18,7 @@
"use strict";
var throwIf = require('throwIf');
var invariant = require('invariant');
var traverseAllChildren = require('traverseAllChildren');
/**
@@ -27,14 +27,14 @@ var traverseAllChildren = require('traverseAllChildren');
* @param {!string} name String name of key path to child.
*/
function flattenSingleChildIntoContext(traverseContext, child, name) {
// We found a component instance
// We found a component instance.
var result = traverseContext;
if (__DEV__) {
throwIf(
result.hasOwnProperty(name),
traverseAllChildren.DUPLICATE_KEY_ERROR
);
}
invariant(
!result.hasOwnProperty(name),
'flattenChildren(...): Encountered two children with the same key, `%s`. ' +
'Children keys must be unique.',
name
);
result[name] = child;
}
@@ -43,7 +43,7 @@ function flattenSingleChildIntoContext(traverseContext, child, name) {
* @return {!object} flattened children keyed by name.
*/
function flattenChildren(children) {
if (children === null || children === undefined) {
if (children == null) {
return children;
}
var result = {};
+20 -18
View File
@@ -14,36 +14,38 @@
* limitations under the License.
*
* @providesModule keyMirror
* @typechecks static-only
*/
"use strict";
var throwIf = require('throwIf');
var NOT_OBJECT_ERROR = 'NOT_OBJECT_ERROR';
if (__DEV__) {
NOT_OBJECT_ERROR = 'keyMirror only works on objects';
}
var invariant = require('invariant');
/**
* Utility for constructing enums with keys being equal to the associated
* values, even when using advanced key crushing. This is useful for debugging,
* but also for using the values themselves as lookups into the enum.
* Example:
* var COLORS = keyMirror({blue: null, red: null});
* var myColor = COLORS.blue;
* var isColorValid = !!COLORS[myColor]
* Constructs an enumeration with keys equal to their value.
*
* For example:
*
* var COLORS = keyMirror({blue: null, red: null});
* var myColor = COLORS.blue;
* var isColorValid = !!COLORS[myColor];
*
* The last line could not be performed if the values of the generated enum were
* not equal to their keys.
* Input: {key1: val1, key2: val2}
* Output: {key1: key1, key2: key2}
*
* Input: {key1: val1, key2: val2}
* Output: {key1: key1, key2: key2}
*
* @param {object} obj
* @return {object}
*/
var keyMirror = function(obj) {
var ret = {};
var key;
throwIf(!(obj instanceof Object) || Array.isArray(obj), NOT_OBJECT_ERROR);
invariant(
obj instanceof Object && !Array.isArray(obj),
'keyMirror(...): Argument must be an object.'
);
for (key in obj) {
if (!obj.hasOwnProperty(key)) {
continue;
+8 -8
View File
@@ -20,7 +20,7 @@
var PooledClass = require('PooledClass');
var throwIf = require('throwIf');
var invariant = require('invariant');
var traverseAllChildren = require('traverseAllChildren');
var threeArgumentPooler = PooledClass.threeArgumentPooler;
@@ -50,13 +50,13 @@ function mapSingleChildIntoContext(traverseContext, child, name, i) {
var mapFunction = mapBookKeeping.mapFunction;
var mapContext = mapBookKeeping.mapContext;
var mappedChild = mapFunction.call(mapContext, child, name, i);
// We found a component instance
if (__DEV__) {
throwIf(
mapResult.hasOwnProperty(name),
traverseAllChildren.DUPLICATE_KEY_ERROR
);
}
// We found a component instance.
invariant(
!mapResult.hasOwnProperty(name),
'mapAllChildren(...): Encountered two children with the same key, `%s`. ' +
'Children keys must be unique.',
name
);
mapResult[name] = mappedChild;
}
+6 -20
View File
@@ -21,9 +21,8 @@
"use strict";
var keyMirror = require('keyMirror');
var invariant = require('invariant');
var mergeHelpers = require('mergeHelpers');
var throwIf = require('throwIf');
var ArrayStrategies = mergeHelpers.ArrayStrategies;
var checkArrayStrategy = mergeHelpers.checkArrayStrategy;
@@ -33,20 +32,6 @@ var checkMergeObjectArgs = mergeHelpers.checkMergeObjectArgs;
var isTerminal = mergeHelpers.isTerminal;
var normalizeMergeArg = mergeHelpers.normalizeMergeArg;
var ERRORS = keyMirror({
RUN_TIME_ARRAY_MERGE_FAIL: null
});
if (__DEV__) {
ERRORS = {
RUN_TIME_ARRAY_MERGE_FAIL:
"The caller has not supplied an ArrayStrategy. This is supported as " +
"long as the data structures being merged do not contain two Arrays " +
"that must be merged together, which is exactly what has just " +
"happened. Change the call site to supply an Array merge resolver."
};
}
/**
* Every deep merge function must handle merging in each of the following cases
* at every level. We may refer to letters below in implementations. For each
@@ -161,9 +146,10 @@ var mergeSingleFieldDeep = function(one, two, key, arrayStrategy, level) {
if (twoValIsTerminal) { // [E]
one[key] = twoVal;
} else if (twoValIsArray) { // [F]
throwIf(
!ArrayStrategies[arrayStrategy],
ERRORS.RUN_TIME_ARRAY_MERGE_FAIL
invariant(
ArrayStrategies[arrayStrategy],
'mergeDeepInto(...): Attempted to merge two arrays, but a valid ' +
'ArrayStrategy was not specified.'
);
// Else: At this point, the only other valid option is `IndexByIndex`
if (arrayStrategy === ArrayStrategies.Clobber) {
@@ -234,7 +220,7 @@ var mergeSingleFieldDeep = function(one, two, key, arrayStrategy, level) {
*/
var mergeDeepInto = function(one, twoParam, arrayStrategy) {
var two = normalizeMergeArg(twoParam);
checkArrayStrategy(arrayStrategy); // Will be checked twice, for now
checkArrayStrategy(arrayStrategy); // Will be checked twice, for now.
mergeDeepIntoObjects(one, two, arrayStrategy, 0);
};
+24 -49
View File
@@ -20,8 +20,8 @@
"use strict";
var invariant = require('invariant');
var keyMirror = require('keyMirror');
var throwIf = require('throwIf');
/**
* Maximum number of levels to traverse. Will catch circular structures.
@@ -29,41 +29,6 @@ var throwIf = require('throwIf');
*/
var MAX_MERGE_DEPTH = 36;
var ERRORS = keyMirror({
MERGE_ARRAY_FAIL: null,
MERGE_CORE_FAILURE: null,
MERGE_TYPE_USAGE_FAILURE: null,
MERGE_DEEP_MAX_LEVELS: null,
MERGE_DEEP_NO_ARR_STRATEGY: null
});
if (__DEV__) {
ERRORS = {
MERGE_ARRAY_FAIL:
'Unsupported type passed to a merge function. You may have passed a ' +
'structure that contains an array and the merge function does not know ' +
'how to merge arrays. ',
MERGE_CORE_FAILURE:
'Critical assumptions about the merge functions have been violated. ' +
'This is the fault of the merge functions themselves, not necessarily ' +
'the callers.',
MERGE_TYPE_USAGE_FAILURE:
'Calling merge function with invalid types. You may call merge ' +
'functions (non-array non-terminal) OR (null/undefined) arguments. ' +
'mergeInto functions have the same requirements but with an added ' +
'restriction that the first parameter must not be null/undefined.',
MERGE_DEEP_MAX_LEVELS:
'Maximum deep merge depth exceeded. You may attempting to merge ' +
'circular structures in an unsupported way.',
MERGE_DEEP_NO_ARR_STRATEGY:
'You must provide an array strategy to deep merge functions to ' +
'instruct the deep merge how to resolve merging two arrays.'
};
}
/**
* We won't worry about edge cases like new String('x') or new Boolean(true).
* Functions are considered terminals, and arrays are not.
@@ -99,9 +64,11 @@ var mergeHelpers = {
* @param {*} two Array to merge from.
*/
checkMergeArrayArgs: function(one, two) {
throwIf(
!Array.isArray(one) || !Array.isArray(two),
ERRORS.MERGE_CORE_FAILURE
invariant(
Array.isArray(one) && Array.isArray(two),
'Critical assumptions about the merge functions have been violated. ' +
'This is the fault of the merge functions themselves, not necessarily ' +
'the callers.'
);
},
@@ -118,7 +85,12 @@ var mergeHelpers = {
* @param {*} arg
*/
checkMergeObjectArg: function(arg) {
throwIf(isTerminal(arg) || Array.isArray(arg), ERRORS.MERGE_CORE_FAILURE);
invariant(
!isTerminal(arg) && !Array.isArray(arg),
'Critical assumptions about the merge functions have been violated. ' +
'This is the fault of the merge functions themselves, not necessarily ' +
'the callers.'
);
},
/**
@@ -128,19 +100,23 @@ var mergeHelpers = {
* @param {number} Level of recursion to validate against maximum.
*/
checkMergeLevel: function(level) {
throwIf(level >= MAX_MERGE_DEPTH, ERRORS.MERGE_DEEP_MAX_LEVELS);
invariant(
level < MAX_MERGE_DEPTH,
'Maximum deep merge depth exceeded. You may be attempting to merge ' +
'circular structures in an unsupported way.'
);
},
/**
* Checks that a merge was not given a circular object or an object that had
* too great of depth.
* Checks that the supplied merge strategy is valid.
*
* @param {number} Level of recursion to validate against maximum.
* @param {string} Array merge strategy.
*/
checkArrayStrategy: function(strategy) {
throwIf(
strategy !== undefined && !(strategy in mergeHelpers.ArrayStrategies),
ERRORS.MERGE_DEEP_NO_ARR_STRATEGY
invariant(
strategy === undefined || strategy in mergeHelpers.ArrayStrategies,
'You must provide an array strategy to deep merge functions to ' +
'instruct the deep merge how to resolve merging two arrays.'
);
},
@@ -154,9 +130,8 @@ var mergeHelpers = {
ArrayStrategies: keyMirror({
Clobber: true,
IndexByIndex: true
}),
})
ERRORS: ERRORS
};
module.exports = mergeHelpers;
+6 -23
View File
@@ -21,22 +21,7 @@
var ReactComponent = require('ReactComponent');
var ReactTextComponent = require('ReactTextComponent');
var throwIf = require('throwIf');
/**
* @polyFill Array.isArray
*/
var DUPLICATE_KEY_ERROR = 'DUPLICATE_KEY_ERROR';
var INVALID_CHILD = 'INVALID_CHILD';
if (__DEV__) {
INVALID_CHILD =
'You may not pass a child of that type to a React component. It ' +
'is a common mistake to try to pass a standard browser DOM element ' +
'as a child of a React component.';
DUPLICATE_KEY_ERROR =
'You have two children with identical keys. Make sure that you set the ' +
'"key" property to a unique value such as a row ID.';
}
var invariant = require('invariant');
/**
* TODO: Test that:
@@ -88,7 +73,11 @@ var traverseAllChildrenImpl =
subtreeCount = 1;
} else {
if (type === 'object') {
throwIf(children && children.nodeType === 1, INVALID_CHILD);
invariant(
children || children.nodeType !== 1,
'traverseAllChildren(...): Encountered an invalid child; DOM ' +
'elements are not valid children of React components.'
);
for (var key in children) {
if (children.hasOwnProperty(key)) {
subtreeCount += traverseAllChildrenImpl(
@@ -135,10 +124,4 @@ function traverseAllChildren(children, callback, traverseContext) {
}
}
/**
* Export the error code for use in other walking/mapping code.
*/
traverseAllChildren.DUPLICATE_KEY_ERROR = DUPLICATE_KEY_ERROR;
module.exports = traverseAllChildren;