Add component stack to invalid element type warning (#8495)
* Show Source Error Addemden if __source available * Add Parent Stack on invalid element type * refactor to use normalizeCodeLocInfo * Remove ( ) from addendum
Jirat Ki committed
Jan 9, 2017 at 22:26 UTC
fb7e49439feecd401029f24bf401f22b235c3947
3 files changed
+51
-31
src/isomorphic/classic/element/ReactElementValidator.js
+24
-1
@@ -39,6 +39,20 @@ function getDeclarationErrorAddendum() {
39
return '';
40
}
41
42
+function getSourceInfoErrorAddendum(elementProps) {
43
+ if (
44
+ elementProps !== null &&
45
+ elementProps !== undefined &&
46
+ elementProps.__source !== undefined
47
+ ) {
48
+ var source = elementProps.__source;
49
+ var fileName = source.fileName.replace(/^.*[\\\/]/, '');
50
+ var lineNumber = source.lineNumber;
51
+ return ' Check your code at ' + fileName + ':' + lineNumber + '.';
52
+ }
53
+ return '';
54
+}
55
+
56
/**
57
* Warn if there's no key explicitly set on dynamic arrays of children or
58
* object keys are not valid. This allows us to keep track of children between
@@ -206,7 +220,16 @@ var ReactElementValidator = {
220
' You likely forgot to export your component from the file ' +
221
'it\'s defined in.';
222
}
209
- info += getDeclarationErrorAddendum();
223
+
224
+ var sourceInfo = getSourceInfoErrorAddendum(props);
225
+ if (sourceInfo) {
226
+ info += sourceInfo;
227
+ } else {
228
+ info += getDeclarationErrorAddendum();
229
+ }
230
+
231
+ info += ReactComponentTreeHook.getCurrentStackAddendum();
232
+
233
warning(
234
false,
235
'React.createElement: type is invalid -- expected a string (for ' +
src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js
+5
-4
@@ -20,7 +20,7 @@ var ReactTestUtils;
20
21
describe('ReactElementValidator', () => {
22
function normalizeCodeLocInfo(str) {
23
- return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
23
+ return str && str.replace(/at .+?:\d+/g, 'at **');
24
}
25
26
var ComponentClass;
@@ -349,7 +349,8 @@ describe('ReactElementValidator', () => {
349
expectDev(console.error.calls.argsFor(0)[0]).toBe(
350
'Warning: React.createElement: type is invalid -- expected a string ' +
351
'(for built-in components) or a class/function (for composite ' +
352
- 'components) but got: null. Check the render method of `ParentComp`.'
352
+ 'components) but got: null. Check the render method of `ParentComp`.' +
353
+ '\n in ParentComp'
354
);
355
});
356
@@ -546,11 +547,11 @@ describe('ReactElementValidator', () => {
547
var Foo = undefined;
548
void <Foo>{[<div />]}</Foo>;
549
expectDev(console.error.calls.count()).toBe(1);
549
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
550
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
551
'Warning: React.createElement: type is invalid -- expected a string ' +
552
'(for built-in components) or a class/function (for composite ' +
553
'components) but got: undefined. You likely forgot to export your ' +
553
- 'component from the file it\'s defined in.'
554
+ 'component from the file it\'s defined in. Check your code at **.'
555
);
556
});
557
src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js
+22
-26
@@ -19,6 +19,10 @@ var ReactDOM;
19
var ReactTestUtils;
20
21
describe('ReactJSXElementValidator', () => {
22
+ function normalizeCodeLocInfo(str) {
23
+ return str && str.replace(/at .+?:\d+/g, 'at **');
24
+ }
25
+
26
var Component;
27
var RequiredPropComponent;
28
@@ -194,9 +198,7 @@ describe('ReactJSXElementValidator', () => {
198
}
199
}
200
ReactTestUtils.renderIntoDocument(<ParentComp />);
197
- expect(
198
- console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)')
199
- ).toBe(
201
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
202
'Warning: Failed prop type: ' +
203
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
204
'expected `string`.\n' +
@@ -232,9 +234,7 @@ describe('ReactJSXElementValidator', () => {
234
expect(console.error.calls.count()).toBe(1);
235
// The warning should have the full stack with line numbers.
236
// If it doesn't, it means we're using information from the old element.
235
- expect(
236
- console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)')
237
- ).toBe(
237
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
238
'Warning: Failed prop type: ' +
239
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
240
'expected `string`.\n' +
@@ -256,26 +256,30 @@ describe('ReactJSXElementValidator', () => {
256
void <True />;
257
void <Num />;
258
expectDev(console.error.calls.count()).toBe(4);
259
- expectDev(console.error.calls.argsFor(0)[0]).toBe(
259
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
260
'Warning: React.createElement: type is invalid -- expected a string ' +
261
'(for built-in components) or a class/function (for composite ' +
262
'components) but got: undefined. You likely forgot to export your ' +
263
- 'component from the file it\'s defined in.'
263
+ 'component from the file it\'s defined in. ' +
264
+ 'Check your code at **.'
265
);
265
- expectDev(console.error.calls.argsFor(1)[0]).toBe(
266
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
267
'Warning: React.createElement: type is invalid -- expected a string ' +
268
'(for built-in components) or a class/function (for composite ' +
268
- 'components) but got: null.'
269
+ 'components) but got: null. ' +
270
+ 'Check your code at **.'
271
);
270
- expectDev(console.error.calls.argsFor(2)[0]).toBe(
272
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
273
'Warning: React.createElement: type is invalid -- expected a string ' +
274
'(for built-in components) or a class/function (for composite ' +
273
- 'components) but got: boolean.'
275
+ 'components) but got: boolean. ' +
276
+ 'Check your code at **.'
277
);
275
- expectDev(console.error.calls.argsFor(3)[0]).toBe(
278
+ expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(3)[0])).toBe(
279
'Warning: React.createElement: type is invalid -- expected a string ' +
280
'(for built-in components) or a class/function (for composite ' +
278
- 'components) but got: number.'
281
+ 'components) but got: number. ' +
282
+ 'Check your code at **.'
283
);
284
void <Div />;
285
expectDev(console.error.calls.count()).toBe(4);
@@ -289,9 +293,7 @@ describe('ReactJSXElementValidator', () => {
293
ReactTestUtils.renderIntoDocument(<RequiredPropComponent />);
294
295
expectDev(console.error.calls.count()).toBe(1);
292
- expect(
293
- console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)')
294
- ).toBe(
296
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
297
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
298
'`RequiredPropComponent`, but its value is `null`.\n' +
299
' in RequiredPropComponent (at **)'
@@ -304,9 +306,7 @@ describe('ReactJSXElementValidator', () => {
306
ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={null} />);
307
308
expectDev(console.error.calls.count()).toBe(1);
307
- expect(
308
- console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)')
309
- ).toBe(
309
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
310
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
311
'`RequiredPropComponent`, but its value is `null`.\n' +
312
' in RequiredPropComponent (at **)'
@@ -320,18 +320,14 @@ describe('ReactJSXElementValidator', () => {
320
ReactTestUtils.renderIntoDocument(<RequiredPropComponent prop={42} />);
321
322
expectDev(console.error.calls.count()).toBe(2);
323
- expect(
324
- console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)')
325
- ).toBe(
323
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
324
'Warning: Failed prop type: ' +
325
'The prop `prop` is marked as required in `RequiredPropComponent`, but ' +
326
'its value is `undefined`.\n' +
327
' in RequiredPropComponent (at **)'
328
);
329
332
- expect(
333
- console.error.calls.argsFor(1)[0].replace(/\(at .+?:\d+\)/g, '(at **)')
334
- ).toBe(
330
+ expect(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
331
'Warning: Failed prop type: ' +
332
'Invalid prop `prop` of type `number` supplied to ' +
333
'`RequiredPropComponent`, expected `string`.\n' +