Skip to content

Commit ae30e3d

Browse files
committed
Replace console.warn/error with a custom wrapper at build time
1 parent 782c7b6 commit ae30e3d

33 files changed

+391
-370
lines changed

.eslintrc.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ module.exports = {
3333
'comma-dangle': [ERROR, 'always-multiline'],
3434
'consistent-return': OFF,
3535
'dot-location': [ERROR, 'property'],
36-
'dot-notation': ERROR,
36+
// We use console['error']() as a signal to not transform it:
37+
'dot-notation': [ERROR, {allowPattern: '^(error|warn)$'}],
3738
'eol-last': ERROR,
3839
eqeqeq: [ERROR, 'allow-null'],
3940
indent: OFF,
@@ -135,6 +136,18 @@ module.exports = {
135136
'jest/valid-expect-in-promise': ERROR,
136137
},
137138
},
139+
{
140+
files: [
141+
'**/__tests__/**/*.js',
142+
'scripts/**/*.js',
143+
'packages/*/npm/**/*.js',
144+
'packages/react-devtools*/**/*.js'
145+
],
146+
rules: {
147+
'react-internal/no-production-logging': OFF,
148+
'react-internal/warning-args': OFF,
149+
},
150+
},
138151
{
139152
files: ['packages/react-native-renderer/**/*.js'],
140153
globals: {

packages/legacy-events/ResponderEventPlugin.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,11 @@ const ResponderEventPlugin = {
515515
if (trackedTouchCount >= 0) {
516516
trackedTouchCount -= 1;
517517
} else {
518-
console.warn(
519-
'Ended a touch event which was not counted in `trackedTouchCount`.',
520-
);
518+
if (__DEV__) {
519+
console.warn(
520+
'Ended a touch event which was not counted in `trackedTouchCount`.',
521+
);
522+
}
521523
return null;
522524
}
523525
}

packages/legacy-events/ResponderTouchHistoryStore.js

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,15 @@ function recordTouchMove(touch: Touch): void {
129129
touchRecord.currentTimeStamp = timestampForTouch(touch);
130130
touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
131131
} else {
132-
console.warn(
133-
'Cannot record touch move without a touch start.\n' + 'Touch Move: %s\n',
134-
'Touch Bank: %s',
135-
printTouch(touch),
136-
printTouchBank(),
137-
);
132+
if (__DEV__) {
133+
console.warn(
134+
'Cannot record touch move without a touch start.\n' +
135+
'Touch Move: %s\n' +
136+
'Touch Bank: %s',
137+
printTouch(touch),
138+
printTouchBank(),
139+
);
140+
}
138141
}
139142
}
140143

@@ -150,12 +153,15 @@ function recordTouchEnd(touch: Touch): void {
150153
touchRecord.currentTimeStamp = timestampForTouch(touch);
151154
touchHistory.mostRecentTimeStamp = timestampForTouch(touch);
152155
} else {
153-
console.warn(
154-
'Cannot record touch end without a touch start.\n' + 'Touch End: %s\n',
155-
'Touch Bank: %s',
156-
printTouch(touch),
157-
printTouchBank(),
158-
);
156+
if (__DEV__) {
157+
console.warn(
158+
'Cannot record touch end without a touch start.\n' +
159+
'Touch End: %s\n' +
160+
'Touch Bank: %s',
161+
printTouch(touch),
162+
printTouchBank(),
163+
);
164+
}
159165
}
160166
}
161167

packages/react-dom/src/client/ReactDOM.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ if (__DEV__) {
211211
const protocol = window.location.protocol;
212212
// Don't warn in exotic cases like chrome-extension://.
213213
if (/^(https?|file):$/.test(protocol)) {
214+
// eslint-disable-next-line react-internal/no-production-logging
214215
console.info(
215216
'%cDownload the React DevTools ' +
216217
'for a better development experience: ' +

packages/react-dom/src/test-utils/ReactTestUtilsAct.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ function act(callback: () => Thenable) {
7979
if (!__DEV__) {
8080
if (didWarnAboutUsingActInProd === false) {
8181
didWarnAboutUsingActInProd = true;
82+
// eslint-disable-next-line react-internal/no-production-logging
8283
console.error(
8384
'act(...) is not supported in production builds of React, and might not behave as expected.',
8485
);

packages/react-interactions/events/src/dom/testing-library/domEnvironment.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ export function hasPointerEvent() {
2222
export function setPointerEvent(bool) {
2323
const pointerCaptureFn = name => id => {
2424
if (typeof id !== 'number') {
25-
console.error(`A pointerId must be passed to "${name}"`);
25+
if (__DEV__) {
26+
console.error('A pointerId must be passed to "%s"', name);
27+
}
2628
}
2729
};
2830
global.PointerEvent = bool ? emptyFunction : undefined;

packages/react-is/src/ReactIs.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import {
2525
REACT_SUSPENSE_TYPE,
2626
} from 'shared/ReactSymbols';
2727
import isValidElementType from 'shared/isValidElementType';
28-
import lowPriorityWarningWithoutStack from 'shared/lowPriorityWarningWithoutStack';
2928

3029
export function typeOf(object: any) {
3130
if (typeof object === 'object' && object !== null) {
@@ -88,7 +87,7 @@ export function isAsyncMode(object: any) {
8887
if (__DEV__) {
8988
if (!hasWarnedAboutDeprecatedIsAsyncMode) {
9089
hasWarnedAboutDeprecatedIsAsyncMode = true;
91-
lowPriorityWarningWithoutStack(
90+
console.warn(
9291
'The ReactIs.isAsyncMode() alias has been deprecated, ' +
9392
'and will be removed in React 17+. Update your code to use ' +
9493
'ReactIs.isConcurrentMode() instead. It has the exact same API.',

packages/react-native-renderer/src/NativeMethodsMixinUtils.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,19 @@ export function throwOnStylesProp(component: any, props: any) {
6666
}
6767

6868
export function warnForStyleProps(props: any, validAttributes: any) {
69-
for (const key in validAttributes.style) {
70-
if (!(validAttributes[key] || props[key] === undefined)) {
71-
console.error(
72-
'You are setting the style `{ ' +
73-
key +
74-
': ... }` as a prop. You ' +
75-
'should nest it in a style object. ' +
76-
'E.g. `{ style: { ' +
77-
key +
78-
': ... } }`',
79-
);
69+
if (__DEV__) {
70+
for (const key in validAttributes.style) {
71+
if (!(validAttributes[key] || props[key] === undefined)) {
72+
console.error(
73+
'You are setting the style `{ %s' +
74+
': ... }` as a prop. You ' +
75+
'should nest it in a style object. ' +
76+
'E.g. `{ style: { %s' +
77+
': ... } }`',
78+
key,
79+
key,
80+
);
81+
}
8082
}
8183
}
8284
}

packages/react-noop-renderer/src/createReactNoop.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
638638
if (!__DEV__) {
639639
if (didWarnAboutUsingActInProd === false) {
640640
didWarnAboutUsingActInProd = true;
641+
// eslint-disable-next-line react-internal/no-production-logging
641642
console.error(
642643
'act(...) is not supported in production builds of React, and might not behave as expected.',
643644
);
@@ -1106,6 +1107,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
11061107
const root = roots.get(rootID);
11071108
const rootContainer = rootContainers.get(rootID);
11081109
if (!root || !rootContainer) {
1110+
// eslint-disable-next-line react-internal/no-production-logging
11091111
console.log('Nothing rendered yet.');
11101112
return;
11111113
}
@@ -1208,6 +1210,7 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
12081210
log('FIBERS:');
12091211
logFiber(root.current, 0);
12101212

1213+
// eslint-disable-next-line react-internal/no-production-logging
12111214
console.log(...bufferedLog);
12121215
},
12131216

packages/react-reconciler/src/ReactFiberErrorLogger.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ export function logCapturedError(capturedError: CapturedError): void {
2020
return;
2121
}
2222

23-
// Prevent it from being seen by Babel transform.
24-
const rawConsoleError = console.error;
25-
2623
const error = (capturedError.error: any);
2724
if (__DEV__) {
2825
const {
@@ -47,7 +44,7 @@ export function logCapturedError(capturedError: CapturedError): void {
4744
// been accidental, we'll surface it anyway.
4845
// However, the browser would have silenced the original error
4946
// so we'll print it first, and then print the stack addendum.
50-
rawConsoleError(error);
47+
console['error'](error); // Don't transform to our wrapper
5148
// For a more detailed description of this block, see:
5249
// https://github.com/facebook/react/pull/13384
5350
}
@@ -81,11 +78,12 @@ export function logCapturedError(capturedError: CapturedError): void {
8178
// We don't include the original error message and JS stack because the browser
8279
// has already printed it. Even if the application swallows the error, it is still
8380
// displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils.
84-
rawConsoleError(combinedMessage);
81+
console['error'](combinedMessage); // Don't transform to our wrapper
8582
} else {
8683
// In production, we print the error directly.
8784
// This will include the message, the JS stack, and anything the browser wants to show.
8885
// We pass the error object instead of custom message so that the browser displays the error natively.
89-
rawConsoleError(error);
86+
// eslint-disable-next-line react-internal/no-production-logging
87+
console['error'](error); // Don't transform to our wrapper
9088
}
9189
}

packages/react-test-renderer/src/ReactTestRendererAct.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ function act(callback: () => Thenable) {
6060
if (!__DEV__) {
6161
if (didWarnAboutUsingActInProd === false) {
6262
didWarnAboutUsingActInProd = true;
63+
// eslint-disable-next-line react-internal/no-production-logging
6364
console.error(
6465
'act(...) is not supported in production builds of React, and might not behave as expected.',
6566
);

packages/react/src/__tests__/ReactClassEquivalence-test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ describe('ReactClassEquivalence', () => {
2828
function runJest(testFile) {
2929
const cwd = process.cwd();
3030
const extension = process.platform === 'win32' ? '.cmd' : '';
31-
const result = spawnSync('yarn' + extension, ['test', testFile], {
31+
const command = __DEV__ ? 'test' : 'test-prod';
32+
const result = spawnSync('yarn' + extension, [command, testFile], {
3233
cwd,
3334
env: Object.assign({}, process.env, {
3435
REACT_CLASS_EQUIVALENCE_TEST: 'true',

packages/scheduler/src/SchedulerProfiling.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@ function logEvent(entries) {
6969
if (eventLogIndex + 1 > eventLogSize) {
7070
eventLogSize *= 2;
7171
if (eventLogSize > MAX_EVENT_LOG_SIZE) {
72-
console.error(
72+
// Using console['error'] to evade Babel and ESLint
73+
console['error'](
7374
"Scheduler Profiling: Event log exceeded maximum size. Don't " +
7475
'forget to call `stopLoggingProfilingEvents()`.',
7576
);

packages/scheduler/src/__tests__/SchedulerProfiling-test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ Task 1 [Normal] │ █████████
544544
}
545545

546546
expect(console.error).toHaveBeenCalledTimes(1);
547-
expect(console.error.calls.argsFor(0)[0]).toContain(
547+
expect(console.error.calls.argsFor(0)[0]).toBe(
548548
"Scheduler Profiling: Event log exceeded maximum size. Don't forget " +
549549
'to call `stopLoggingProfilingEvents()`.',
550550
);

packages/scheduler/src/forks/SchedulerHostConfig.default.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,16 @@ if (
8181
const cancelAnimationFrame = window.cancelAnimationFrame;
8282
// TODO: Remove fb.me link
8383
if (typeof requestAnimationFrame !== 'function') {
84-
console.error(
84+
// Using console['error'] to evade Babel and ESLint
85+
console['error'](
8586
"This browser doesn't support requestAnimationFrame. " +
8687
'Make sure that you load a ' +
8788
'polyfill in older browsers. https://fb.me/react-polyfills',
8889
);
8990
}
9091
if (typeof cancelAnimationFrame !== 'function') {
91-
console.error(
92+
// Using console['error'] to evade Babel and ESLint
93+
console['error'](
9294
"This browser doesn't support cancelAnimationFrame. " +
9395
'Make sure that you load a ' +
9496
'polyfill in older browsers. https://fb.me/react-polyfills',
@@ -169,7 +171,8 @@ if (
169171

170172
forceFrameRate = function(fps) {
171173
if (fps < 0 || fps > 125) {
172-
console.error(
174+
// Using console['error'] to evade Babel and ESLint
175+
console['error'](
173176
'forceFrameRate takes a positive int between 0 and 125, ' +
174177
'forcing framerates higher than 125 fps is not unsupported',
175178
);
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import ReactSharedInternals from 'shared/ReactSharedInternals';
9+
10+
// In DEV, calls to console.warn and console.error get replaced
11+
// by calls to these methods by a Babel plugin.
12+
//
13+
// In PROD (or in packages without access to React internals),
14+
// they are left as they are instead.
15+
16+
export function warn(format, ...args) {
17+
if (__DEV__) {
18+
printWarning('warn', format, args);
19+
}
20+
}
21+
22+
export function error(format, ...args) {
23+
if (__DEV__) {
24+
printWarning('error', format, args);
25+
}
26+
}
27+
28+
function printWarning(level, format, args) {
29+
if (__DEV__) {
30+
const hasExistingStack =
31+
args.length > 0 &&
32+
typeof args[args.length - 1] === 'string' &&
33+
args[args.length - 1].indexOf('\n in') === 0;
34+
35+
if (!hasExistingStack) {
36+
const ReactDebugCurrentFrame =
37+
ReactSharedInternals.ReactDebugCurrentFrame;
38+
const stack = ReactDebugCurrentFrame.getStackAddendum();
39+
if (stack !== '') {
40+
format += '%s';
41+
args = args.concat([stack]);
42+
}
43+
}
44+
45+
const argsWithFormat = args.map(item => '' + item);
46+
// Careful: RN currently depends on this prefix
47+
argsWithFormat.unshift('Warning: ' + format);
48+
// We intentionally don't use spread (or .apply) directly because it
49+
// breaks IE9: https://github.com/facebook/react/issues/13610
50+
// eslint-disable-next-line react-internal/no-production-logging
51+
Function.prototype.apply.call(console[level], console, argsWithFormat);
52+
53+
try {
54+
// --- Welcome to debugging React ---
55+
// This error was thrown as a convenience so that you can use this stack
56+
// to find the callsite that caused this warning to fire.
57+
let argIndex = 0;
58+
const message =
59+
'Warning: ' + format.replace(/%s/g, () => args[argIndex++]);
60+
throw new Error(message);
61+
} catch (x) {}
62+
}
63+
}

packages/shared/enqueueTask.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77
* @flow
88
*/
99

10-
import warningWithoutStack from 'shared/warningWithoutStack';
11-
1210
let didWarnAboutMessageChannel = false;
1311
let enqueueTask;
1412
try {
@@ -28,7 +26,7 @@ try {
2826
if (didWarnAboutMessageChannel === false) {
2927
didWarnAboutMessageChannel = true;
3028
if (typeof MessageChannel === 'undefined') {
31-
warningWithoutStack(
29+
console.error(
3230
'This browser does not have a MessageChannel implementation, ' +
3331
'so enqueuing tasks via await act(async () => ...) will fail. ' +
3432
'Please file an issue at https://github.com/facebook/react/issues ' +

packages/shared/forks/warningWithoutStack.www.js renamed to packages/shared/forks/consoleWithStackDev.www.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
// This refers to a WWW module.
99
const warningWWW = require('warning');
1010

11-
export default function warningWithoutStack(format, ...args) {
11+
export function warn(format, ...args) {
12+
// TODO: different level.
13+
return warningWWW(false, format, ...args);
14+
}
15+
16+
export function error(format, ...args) {
1217
return warningWWW(false, format, ...args);
1318
}

packages/shared/forks/lowPriorityWarningWithoutStack.www.js

Lines changed: 0 additions & 13 deletions
This file was deleted.

0 commit comments

Comments
 (0)