Replace `wrap-warning-with-env-check` with an eslint plugin (#17540)
* Replace Babel plugin with an ESLint plugin * Fix ESLint rule violations * Move shared conditions higher * Test formatting nits * Tweak ESLint rule * Bugfix: inside else branch, 'if' tests are not satisfactory * Use a stricter check for exactly if (__DEV__) This makes it easier to see what's going on and matches dominant style in the codebase. * Fix remaining files after stricter check
This commit is contained in:
parent
acfe4b21b2
commit
b43eec7eaa
|
@ -93,6 +93,7 @@ module.exports = {
|
|||
'react-internal/no-primitive-constructors': ERROR,
|
||||
'react-internal/no-to-warn-dev-within-to-throw': ERROR,
|
||||
'react-internal/warning-and-invariant-args': ERROR,
|
||||
'react-internal/no-production-logging': ERROR,
|
||||
},
|
||||
|
||||
overrides: [
|
||||
|
|
|
@ -36,6 +36,7 @@ export function createSubscription<Property, Value>(
|
|||
}> {
|
||||
const {getCurrentValue, subscribe} = config;
|
||||
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
typeof getCurrentValue === 'function',
|
||||
'Subscription must specify a getCurrentValue function',
|
||||
|
@ -44,6 +45,7 @@ export function createSubscription<Property, Value>(
|
|||
typeof subscribe === 'function',
|
||||
'Subscription must specify a subscribe function',
|
||||
);
|
||||
}
|
||||
|
||||
type Props = {
|
||||
children: (value: Value) => React$Element<any>,
|
||||
|
|
|
@ -283,9 +283,9 @@ function getPooledWarningPropertyDefinition(propName, getVal) {
|
|||
}
|
||||
|
||||
function warn(action, result) {
|
||||
const warningCondition = false;
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
warningCondition,
|
||||
false,
|
||||
"This synthetic event is reused for performance reasons. If you're seeing this, " +
|
||||
"you're %s `%s` on a released/nullified synthetic event. %s. " +
|
||||
'If you must keep the original synthetic event around, use event.persist(). ' +
|
||||
|
@ -296,6 +296,7 @@ function getPooledWarningPropertyDefinition(propName, getVal) {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
|
||||
const EventConstructor = this;
|
||||
|
|
|
@ -142,6 +142,7 @@ const ReactDOM: Object = {
|
|||
// Temporary alias since we already shipped React 16 RC with it.
|
||||
// TODO: remove in React 17.
|
||||
unstable_createPortal(...args) {
|
||||
if (__DEV__) {
|
||||
if (!didWarnAboutUnstableCreatePortal) {
|
||||
didWarnAboutUnstableCreatePortal = true;
|
||||
lowPriorityWarningWithoutStack(
|
||||
|
@ -152,6 +153,7 @@ const ReactDOM: Object = {
|
|||
'but without the "unstable_" prefix.',
|
||||
);
|
||||
}
|
||||
}
|
||||
return createPortal(...args);
|
||||
},
|
||||
|
||||
|
|
|
@ -40,6 +40,7 @@ const valuePropNames = ['value', 'defaultValue'];
|
|||
* Validation function for `value` and `defaultValue`.
|
||||
*/
|
||||
function checkSelectPropTypes(props) {
|
||||
if (__DEV__) {
|
||||
ReactControlledValuePropTypes.checkPropTypes('select', props);
|
||||
|
||||
for (let i = 0; i < valuePropNames.length; i++) {
|
||||
|
@ -67,6 +68,7 @@ function checkSelectPropTypes(props) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateOptions(
|
||||
node: HTMLSelectElement,
|
||||
|
|
|
@ -687,6 +687,7 @@ function resolve(
|
|||
);
|
||||
}
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'%s.getChildContext(): childContextTypes must be defined in order to ' +
|
||||
|
@ -695,6 +696,7 @@ function resolve(
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (childContext) {
|
||||
context = Object.assign({}, context, childContext);
|
||||
}
|
||||
|
|
|
@ -392,7 +392,6 @@ export function useLayoutEffect(
|
|||
) {
|
||||
if (__DEV__) {
|
||||
currentHookNameInDev = 'useLayoutEffect';
|
||||
}
|
||||
warning(
|
||||
false,
|
||||
'useLayoutEffect does nothing on the server, because its effect cannot ' +
|
||||
|
@ -403,6 +402,7 @@ export function useLayoutEffect(
|
|||
'See https://fb.me/react-uselayouteffect-ssr for common fixes.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function dispatchAction<A>(
|
||||
componentIdentity: Object,
|
||||
|
|
|
@ -128,6 +128,7 @@ export function validateShorthandPropertyCollisionInDev(
|
|||
styleUpdates,
|
||||
nextStyles,
|
||||
) {
|
||||
if (__DEV__) {
|
||||
if (!warnAboutShorthandPropertyCollision) {
|
||||
return;
|
||||
}
|
||||
|
@ -162,3 +163,4 @@ export function validateShorthandPropertyCollisionInDev(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ const rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
|
|||
const hasOwnProperty = Object.prototype.hasOwnProperty;
|
||||
|
||||
function validateProperty(tagName, name) {
|
||||
if (__DEV__) {
|
||||
if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) {
|
||||
return true;
|
||||
}
|
||||
|
@ -76,11 +77,13 @@ function validateProperty(tagName, name) {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function warnInvalidARIAProps(type, props) {
|
||||
if (__DEV__) {
|
||||
const invalidProps = [];
|
||||
|
||||
for (const key in props) {
|
||||
|
@ -112,6 +115,7 @@ function warnInvalidARIAProps(type, props) {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function validateProperties(type, props) {
|
||||
if (isCustomComponent(type, props)) {
|
||||
|
|
|
@ -10,6 +10,7 @@ import warning from 'shared/warning';
|
|||
let didWarnValueNull = false;
|
||||
|
||||
export function validateProperties(type, props) {
|
||||
if (__DEV__) {
|
||||
if (type !== 'input' && type !== 'textarea' && type !== 'select') {
|
||||
return;
|
||||
}
|
||||
|
@ -35,3 +36,4 @@ export function validateProperties(type, props) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -255,9 +255,15 @@ if (__DEV__) {
|
|||
}
|
||||
|
||||
const warnUnknownProperties = function(type, props, canUseEventSystem) {
|
||||
if (__DEV__) {
|
||||
const unknownProps = [];
|
||||
for (const key in props) {
|
||||
const isValid = validateProperty(type, key, props[key], canUseEventSystem);
|
||||
const isValid = validateProperty(
|
||||
type,
|
||||
key,
|
||||
props[key],
|
||||
canUseEventSystem,
|
||||
);
|
||||
if (!isValid) {
|
||||
unknownProps.push(key);
|
||||
}
|
||||
|
@ -285,6 +291,7 @@ const warnUnknownProperties = function(type, props, canUseEventSystem) {
|
|||
type,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function validateProperties(type, props, canUseEventSystem) {
|
||||
|
|
|
@ -38,7 +38,8 @@ function sanitizeURL(url: string) {
|
|||
'React has blocked a javascript: URL as a security precaution.%s',
|
||||
__DEV__ ? ReactDebugCurrentFrame.getStackAddendum() : '',
|
||||
);
|
||||
} else if (__DEV__ && !didWarn && isJavaScriptProtocol.test(url)) {
|
||||
} else if (__DEV__) {
|
||||
if (!didWarn && isJavaScriptProtocol.test(url)) {
|
||||
didWarn = true;
|
||||
warning(
|
||||
false,
|
||||
|
@ -49,5 +50,6 @@ function sanitizeURL(url: string) {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default sanitizeURL;
|
||||
|
|
|
@ -359,6 +359,7 @@ const ReactTestUtils = {
|
|||
* @return {object} the ReactTestUtils object (for chaining)
|
||||
*/
|
||||
mockComponent: function(module, mockTagName) {
|
||||
if (__DEV__) {
|
||||
if (!hasWarnedAboutDeprecatedMockComponent) {
|
||||
hasWarnedAboutDeprecatedMockComponent = true;
|
||||
lowPriorityWarningWithoutStack(
|
||||
|
@ -368,6 +369,7 @@ const ReactTestUtils = {
|
|||
'See https://fb.me/test-utils-mock-component for more information.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
mockTagName = mockTagName || module.mockTagName || 'div';
|
||||
|
||||
|
|
|
@ -179,12 +179,14 @@ export default function(
|
|||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: measureLayout on components using NativeMethodsMixin ' +
|
||||
'or ReactNative.NativeComponent is not currently supported in Fabric. ' +
|
||||
'measureLayout must be called on a native ref. Consider using forwardRef.',
|
||||
);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
let relativeNode;
|
||||
|
@ -197,10 +199,12 @@ export default function(
|
|||
}
|
||||
|
||||
if (relativeNode == null) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: ref.measureLayout must be called with a node handle or a ref to a native component.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -243,10 +247,12 @@ export default function(
|
|||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: setNativeProps is not currently supported in Fabric',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -165,11 +165,13 @@ const ReactFabric: ReactFabricType = {
|
|||
handle._nativeTag == null || handle._internalInstanceHandle == null;
|
||||
|
||||
if (invalid) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
!invalid,
|
||||
"dispatchCommand was called with a ref that isn't a " +
|
||||
'native component. Use React.forwardRef to get access to the underlying native component',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -159,10 +159,12 @@ class ReactFabricHostComponent {
|
|||
typeof relativeToNativeNode === 'number' ||
|
||||
!(relativeToNativeNode instanceof ReactFabricHostComponent)
|
||||
) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: ref.measureLayout must be called with a ref to a native component.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -176,10 +178,12 @@ class ReactFabricHostComponent {
|
|||
}
|
||||
|
||||
setNativeProps(nativeProps: Object) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: setNativeProps is not currently supported in Fabric',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -190,12 +190,14 @@ export default function(
|
|||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: measureLayout on components using NativeMethodsMixin ' +
|
||||
'or ReactNative.NativeComponent is not currently supported in Fabric. ' +
|
||||
'measureLayout must be called on a native ref. Consider using forwardRef.',
|
||||
);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
let relativeNode;
|
||||
|
@ -208,10 +210,12 @@ export default function(
|
|||
}
|
||||
|
||||
if (relativeNode == null) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: ref.measureLayout must be called with a node handle or a ref to a native component.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -254,10 +258,12 @@ export default function(
|
|||
}
|
||||
|
||||
if (maybeInstance.canonical) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: setNativeProps is not currently supported in Fabric',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -85,10 +85,12 @@ class ReactNativeFiberHostComponent {
|
|||
}
|
||||
|
||||
if (relativeNode == null) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
false,
|
||||
'Warning: ref.measureLayout must be called with a node handle or a ref to a native component.',
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -172,11 +172,13 @@ const ReactNativeRenderer: ReactNativeType = {
|
|||
|
||||
dispatchCommand(handle: any, command: string, args: Array<any>) {
|
||||
if (handle._nativeTag == null) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
handle._nativeTag != null,
|
||||
"dispatchCommand was called with a ref that isn't a " +
|
||||
'native component. Use React.forwardRef to get access to the underlying native component',
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -232,6 +232,7 @@ function throwOnInvalidObjectType(returnFiber: Fiber, newChild: Object) {
|
|||
}
|
||||
|
||||
function warnOnFunctionType() {
|
||||
if (__DEV__) {
|
||||
const currentComponentErrorInfo =
|
||||
'Functions are not valid as a React child. This may happen if ' +
|
||||
'you return a Component instead of <Component /> from render. ' +
|
||||
|
@ -250,6 +251,7 @@ function warnOnFunctionType() {
|
|||
'Or maybe you meant to call this function rather than return it.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This wrapper function exists because I expect to clone the code in each path
|
||||
// to be able to optimize each path individually by branching early. This needs
|
||||
|
|
|
@ -1397,6 +1397,7 @@ function mountIndeterminateComponent(
|
|||
}
|
||||
|
||||
function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
|
||||
if (__DEV__) {
|
||||
if (Component) {
|
||||
warningWithoutStack(
|
||||
!Component.childContextTypes,
|
||||
|
@ -1474,6 +1475,7 @@ function validateFunctionComponentInDev(workInProgress: Fiber, Component: any) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SUSPENDED_MARKER: SuspenseState = {
|
||||
dehydrated: null,
|
||||
|
|
|
@ -89,7 +89,8 @@ export function injectInternals(internals: Object): boolean {
|
|||
hook.onCommitFiberRoot(rendererID, root, undefined, didError);
|
||||
}
|
||||
} catch (err) {
|
||||
if (__DEV__ && !hasLoggedError) {
|
||||
if (__DEV__) {
|
||||
if (!hasLoggedError) {
|
||||
hasLoggedError = true;
|
||||
warningWithoutStack(
|
||||
false,
|
||||
|
@ -98,12 +99,14 @@ export function injectInternals(internals: Object): boolean {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
onCommitFiberUnmount = fiber => {
|
||||
try {
|
||||
hook.onCommitFiberUnmount(rendererID, fiber);
|
||||
} catch (err) {
|
||||
if (__DEV__ && !hasLoggedError) {
|
||||
if (__DEV__) {
|
||||
if (!hasLoggedError) {
|
||||
hasLoggedError = true;
|
||||
warningWithoutStack(
|
||||
false,
|
||||
|
@ -112,6 +115,7 @@ export function injectInternals(internals: Object): boolean {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} catch (err) {
|
||||
// Catch all errors because it is unsafe to throw during initialization.
|
||||
|
|
|
@ -280,12 +280,14 @@ export function updateContainer(
|
|||
|
||||
callback = callback === undefined ? null : callback;
|
||||
if (callback !== null) {
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(
|
||||
typeof callback === 'function',
|
||||
'render(...): Expected the last optional `callback` argument to be a ' +
|
||||
'function. Instead received: %s.',
|
||||
callback,
|
||||
);
|
||||
}
|
||||
update.callback = callback;
|
||||
}
|
||||
|
||||
|
|
|
@ -1089,13 +1089,15 @@ export function flushDiscreteUpdates() {
|
|||
(executionContext & (BatchedContext | RenderContext | CommitContext)) !==
|
||||
NoContext
|
||||
) {
|
||||
if (__DEV__ && (executionContext & RenderContext) !== NoContext) {
|
||||
if (__DEV__) {
|
||||
if ((executionContext & RenderContext) !== NoContext) {
|
||||
warning(
|
||||
false,
|
||||
'unstable_flushDiscreteUpdates: Cannot flush updates when React is ' +
|
||||
'already rendering.',
|
||||
);
|
||||
}
|
||||
}
|
||||
// We're already rendering, so we can't synchronously flush pending work.
|
||||
// This is probably a nested event dispatch triggered by a lifecycle/effect,
|
||||
// like `el.focus()`. Exit.
|
||||
|
|
|
@ -61,6 +61,7 @@ function areHookInputsEqual(
|
|||
prevDeps: Array<mixed> | null,
|
||||
) {
|
||||
if (prevDeps === null) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'%s received a final argument during this render, but not during ' +
|
||||
|
@ -68,9 +69,11 @@ function areHookInputsEqual(
|
|||
'its type cannot change between renders.',
|
||||
currentHookNameInDev,
|
||||
);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
// Don't bother comparing lengths in prod because these arrays should be
|
||||
// passed inline.
|
||||
if (nextDeps.length !== prevDeps.length) {
|
||||
|
@ -85,6 +88,7 @@ function areHookInputsEqual(
|
|||
`[${prevDeps.join(', ')}]`,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
|
||||
if (is(nextDeps[i], prevDeps[i])) {
|
||||
continue;
|
||||
|
|
|
@ -214,7 +214,8 @@ export function createTextInstance(
|
|||
hostContext: Object,
|
||||
internalInstanceHandle: Object,
|
||||
): TextInstance {
|
||||
if (__DEV__ && enableFlareAPI) {
|
||||
if (__DEV__) {
|
||||
if (enableFlareAPI) {
|
||||
warning(
|
||||
hostContext !== EVENT_COMPONENT_CONTEXT,
|
||||
'validateDOMNesting: React event components cannot have text DOM nodes as children. ' +
|
||||
|
@ -222,6 +223,7 @@ export function createTextInstance(
|
|||
text,
|
||||
);
|
||||
}
|
||||
}
|
||||
return {
|
||||
text,
|
||||
isHidden: false,
|
||||
|
|
|
@ -48,6 +48,7 @@ function hasValidKey(config) {
|
|||
|
||||
function defineKeyPropWarningGetter(props, displayName) {
|
||||
const warnAboutAccessingKey = function() {
|
||||
if (__DEV__) {
|
||||
if (!specialPropKeyWarningShown) {
|
||||
specialPropKeyWarningShown = true;
|
||||
warningWithoutStack(
|
||||
|
@ -59,6 +60,7 @@ function defineKeyPropWarningGetter(props, displayName) {
|
|||
displayName,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
warnAboutAccessingKey.isReactWarning = true;
|
||||
Object.defineProperty(props, 'key', {
|
||||
|
@ -69,6 +71,7 @@ function defineKeyPropWarningGetter(props, displayName) {
|
|||
|
||||
function defineRefPropWarningGetter(props, displayName) {
|
||||
const warnAboutAccessingRef = function() {
|
||||
if (__DEV__) {
|
||||
if (!specialPropRefWarningShown) {
|
||||
specialPropRefWarningShown = true;
|
||||
warningWithoutStack(
|
||||
|
@ -80,6 +83,7 @@ function defineRefPropWarningGetter(props, displayName) {
|
|||
displayName,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
warnAboutAccessingRef.isReactWarning = true;
|
||||
Object.defineProperty(props, 'ref', {
|
||||
|
|
|
@ -194,6 +194,7 @@ function validateChildKeys(node, parentType) {
|
|||
* @param {ReactElement} element
|
||||
*/
|
||||
function validatePropTypes(element) {
|
||||
if (__DEV__) {
|
||||
const type = element.type;
|
||||
if (type === null || type === undefined || typeof type === 'string') {
|
||||
return;
|
||||
|
@ -239,12 +240,14 @@ function validatePropTypes(element) {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a fragment, validate that it can only be provided with fragment props
|
||||
* @param {ReactElement} fragment
|
||||
*/
|
||||
function validateFragmentProps(fragment) {
|
||||
if (__DEV__) {
|
||||
setCurrentlyValidatingElement(fragment);
|
||||
|
||||
const keys = Object.keys(fragment.props);
|
||||
|
@ -267,6 +270,7 @@ function validateFragmentProps(fragment) {
|
|||
|
||||
setCurrentlyValidatingElement(null);
|
||||
}
|
||||
}
|
||||
|
||||
export function jsxWithValidation(
|
||||
type,
|
||||
|
@ -313,6 +317,7 @@ export function jsxWithValidation(
|
|||
typeString = typeof type;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'React.jsx: type is invalid -- expected a string (for ' +
|
||||
|
@ -322,6 +327,7 @@ export function jsxWithValidation(
|
|||
info,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const element = jsxDEV(type, props, key, source, self);
|
||||
|
||||
|
@ -350,6 +356,7 @@ export function jsxWithValidation(
|
|||
Object.freeze(children);
|
||||
}
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'React.jsx: Static children should always be an array. ' +
|
||||
|
@ -357,6 +364,7 @@ export function jsxWithValidation(
|
|||
'Use the Babel transform instead.',
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
validateChildKeys(children, type);
|
||||
}
|
||||
|
@ -364,6 +372,7 @@ export function jsxWithValidation(
|
|||
}
|
||||
|
||||
if (hasOwnProperty.call(props, 'key')) {
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
|
||||
|
@ -371,6 +380,7 @@ export function jsxWithValidation(
|
|||
'E.g. <ComponentName {...props} key={key} />',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (type === REACT_FRAGMENT_TYPE) {
|
||||
validateFragmentProps(element);
|
||||
|
@ -431,6 +441,7 @@ export function createElementWithValidation(type, props, children) {
|
|||
typeString = typeof type;
|
||||
}
|
||||
|
||||
if (__DEV__) {
|
||||
warning(
|
||||
false,
|
||||
'React.createElement: type is invalid -- expected a string (for ' +
|
||||
|
@ -440,6 +451,7 @@ export function createElementWithValidation(type, props, children) {
|
|||
info,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const element = createElement.apply(this, arguments);
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
'use strict';
|
||||
|
||||
let babel = require('@babel/core');
|
||||
let wrapWarningWithEnvCheck = require('../wrap-warning-with-env-check');
|
||||
let wrapWarningWithEnvCheck = require('../lift-warning-conditional-argument');
|
||||
|
||||
function transform(input) {
|
||||
return babel.transform(input, {
|
||||
|
@ -23,7 +23,7 @@ function compare(input, output) {
|
|||
|
||||
let oldEnv;
|
||||
|
||||
describe('wrap-warning-with-env-check', () => {
|
||||
describe('lift-warning-conditional-argument', () => {
|
||||
beforeEach(() => {
|
||||
oldEnv = process.env.NODE_ENV;
|
||||
process.env.NODE_ENV = '';
|
||||
|
@ -36,14 +36,14 @@ describe('wrap-warning-with-env-check', () => {
|
|||
it('should wrap warning calls', () => {
|
||||
compare(
|
||||
"warning(condition, 'a %s b', 'c');",
|
||||
"__DEV__ ? !condition ? warning(false, 'a %s b', 'c') : void 0 : void 0;"
|
||||
"!condition ? warning(false, 'a %s b', 'c') : void 0;"
|
||||
);
|
||||
});
|
||||
|
||||
it('should wrap warningWithoutStack calls', () => {
|
||||
compare(
|
||||
"warningWithoutStack(condition, 'a %s b', 'c');",
|
||||
"__DEV__ ? !condition ? warningWithoutStack(false, 'a %s b', 'c') : void 0 : void 0;"
|
||||
"!condition ? warningWithoutStack(false, 'a %s b', 'c') : void 0;"
|
||||
);
|
||||
});
|
||||
|
|
@ -9,8 +9,6 @@
|
|||
module.exports = function(babel, options) {
|
||||
const t = babel.types;
|
||||
|
||||
const DEV_EXPRESSION = t.identifier('__DEV__');
|
||||
|
||||
const SEEN_SYMBOL = Symbol('expression.seen');
|
||||
|
||||
return {
|
||||
|
@ -34,11 +32,9 @@ module.exports = function(babel, options) {
|
|||
//
|
||||
// into this:
|
||||
//
|
||||
// if (__DEV__) {
|
||||
// if (!condition) {
|
||||
// warning(false, argument, argument);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// The goal is to strip out warning calls entirely in production
|
||||
// and to avoid evaluating the arguments in development.
|
||||
|
@ -49,14 +45,9 @@ module.exports = function(babel, options) {
|
|||
);
|
||||
newNode[SEEN_SYMBOL] = true;
|
||||
path.replaceWith(
|
||||
t.ifStatement(
|
||||
DEV_EXPRESSION,
|
||||
t.blockStatement([
|
||||
t.ifStatement(
|
||||
t.unaryExpression('!', condition),
|
||||
t.expressionStatement(newNode)
|
||||
),
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const rule = require('../no-production-logging');
|
||||
const RuleTester = require('eslint').RuleTester;
|
||||
const ruleTester = new RuleTester();
|
||||
|
||||
ruleTester.run('no-production-logging', rule, {
|
||||
valid: [
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
warning(test, 'Oh no');
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
warningWithoutStack(test, 'Oh no');
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
lowPriorityWarning(test, 'Oh no');
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
lowPriorityWarningWithoutStack(test, 'Oh no');
|
||||
}
|
||||
`,
|
||||
},
|
||||
// This is OK too because it's wrapped outside:
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
if (potato) {
|
||||
while (true) {
|
||||
warning(test, 'Oh no');
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
{
|
||||
code: `
|
||||
var f;
|
||||
if (__DEV__) {
|
||||
f = function() {
|
||||
if (potato) {
|
||||
while (true) {
|
||||
warning(test, 'Oh no');
|
||||
}
|
||||
}
|
||||
};
|
||||
}`,
|
||||
},
|
||||
// Don't do anything with these:
|
||||
{
|
||||
code: 'normalFunctionCall(test);',
|
||||
},
|
||||
{
|
||||
code: 'invariant(test);',
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
normalFunctionCall(test);
|
||||
}
|
||||
`,
|
||||
},
|
||||
// This is OK because of the outer if.
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
if (foo) {
|
||||
if (__DEV__) {
|
||||
} else {
|
||||
warning(test, 'Oh no');
|
||||
}
|
||||
}
|
||||
}`,
|
||||
},
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
code: 'warning(test);',
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warning() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'warningWithoutStack(test)',
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warningWithoutStack() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (potato) {
|
||||
warningWithoutStack(test);
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warningWithoutStack() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'lowPriorityWarning(test);',
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap lowPriorityWarning() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: 'lowPriorityWarningWithoutStack(test)',
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap lowPriorityWarningWithoutStack() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (potato) {
|
||||
lowPriorityWarningWithoutStack(test);
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap lowPriorityWarningWithoutStack() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (__DEV__ || potato && true) {
|
||||
warning(test);
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warning() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (banana && __DEV__ && potato && kitten) {
|
||||
warning(test);
|
||||
}
|
||||
`,
|
||||
// Technically this code is valid but we prefer
|
||||
// explicit standalone __DEV__ blocks that stand out.
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warning() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (!__DEV__) {
|
||||
warning(test);
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warning() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (foo || x && __DEV__) {
|
||||
warning(test);
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warning() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
} else {
|
||||
warning(test);
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warning() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
if (__DEV__) {
|
||||
} else {
|
||||
if (__DEV__) {
|
||||
} else {
|
||||
warning(test);
|
||||
}
|
||||
}
|
||||
`,
|
||||
errors: [
|
||||
{
|
||||
message: `Wrap warning() in an "if (__DEV__) {}" check`,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
|
@ -5,5 +5,6 @@ module.exports = {
|
|||
'no-primitive-constructors': require('./no-primitive-constructors'),
|
||||
'no-to-warn-dev-within-to-throw': require('./no-to-warn-dev-within-to-throw'),
|
||||
'warning-and-invariant-args': require('./warning-and-invariant-args'),
|
||||
'no-production-logging': require('./no-production-logging'),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @emails react-core
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const LOGGER_FN_NAMES = [
|
||||
'warning',
|
||||
'warningWithoutStack',
|
||||
'lowPriorityWarning',
|
||||
'lowPriorityWarningWithoutStack',
|
||||
];
|
||||
|
||||
module.exports = function(context) {
|
||||
function isInDEVBlock(node) {
|
||||
let done = false;
|
||||
while (!done) {
|
||||
let parent = node.parent;
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
parent.type === 'IfStatement' &&
|
||||
node === parent.consequent &&
|
||||
parent.test.type === 'Identifier' &&
|
||||
// This is intentionally strict so we can
|
||||
// see blocks of DEV-only code at once.
|
||||
parent.test.name === '__DEV__'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
node = parent;
|
||||
}
|
||||
}
|
||||
|
||||
function report(node) {
|
||||
context.report({
|
||||
node: node,
|
||||
message: `Wrap {{identifier}}() in an "if (__DEV__) {}" check`,
|
||||
data: {
|
||||
identifier: node.callee.name,
|
||||
},
|
||||
fix: function(fixer) {
|
||||
return [
|
||||
fixer.insertTextBefore(node.parent, `if (__DEV__) {`),
|
||||
fixer.insertTextAfter(node.parent, '}'),
|
||||
];
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const isLoggerFunctionName = name => LOGGER_FN_NAMES.includes(name);
|
||||
|
||||
return {
|
||||
meta: {
|
||||
fixable: 'code',
|
||||
},
|
||||
CallExpression: function(node) {
|
||||
if (!isLoggerFunctionName(node.callee.name)) {
|
||||
return;
|
||||
}
|
||||
if (!isInDEVBlock(node)) {
|
||||
report(node);
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
|
@ -17,7 +17,7 @@ const pathToBabelPluginDevWithCode = require.resolve(
|
|||
'../error-codes/transform-error-messages'
|
||||
);
|
||||
const pathToBabelPluginWrapWarning = require.resolve(
|
||||
'../babel/wrap-warning-with-env-check'
|
||||
'../babel/lift-warning-conditional-argument'
|
||||
);
|
||||
const pathToBabelPluginAsyncToGenerator = require.resolve(
|
||||
'@babel/plugin-transform-async-to-generator'
|
||||
|
|
|
@ -125,7 +125,7 @@ function getBabelConfig(updateBabelOptions, bundleType, filename) {
|
|||
// Minify invariant messages
|
||||
require('../error-codes/transform-error-messages'),
|
||||
// Wrap warning() calls in a __DEV__ check so they are stripped from production.
|
||||
require('../babel/wrap-warning-with-env-check'),
|
||||
require('../babel/lift-warning-conditional-argument'),
|
||||
]),
|
||||
});
|
||||
case RN_OSS_DEV:
|
||||
|
@ -142,7 +142,7 @@ function getBabelConfig(updateBabelOptions, bundleType, filename) {
|
|||
{noMinify: true},
|
||||
],
|
||||
// Wrap warning() calls in a __DEV__ check so they are stripped from production.
|
||||
require('../babel/wrap-warning-with-env-check'),
|
||||
require('../babel/lift-warning-conditional-argument'),
|
||||
]),
|
||||
});
|
||||
case UMD_DEV:
|
||||
|
@ -158,7 +158,7 @@ function getBabelConfig(updateBabelOptions, bundleType, filename) {
|
|||
// Minify invariant messages
|
||||
require('../error-codes/transform-error-messages'),
|
||||
// Wrap warning() calls in a __DEV__ check so they are stripped from production.
|
||||
require('../babel/wrap-warning-with-env-check'),
|
||||
require('../babel/lift-warning-conditional-argument'),
|
||||
]),
|
||||
});
|
||||
default:
|
||||
|
|
Loading…
Reference in New Issue