@samitouri / QOS-React / commits / fb7e49439f

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' +