From b327bc46ced2993d63e7d06c98d88185a0e16762 Mon Sep 17 00:00:00 2001 From: Artur Muller Date: Sun, 7 Mar 2021 23:31:46 +0100 Subject: [PATCH 1/6] Clarify error message in isSerializableProps --- packages/next/lib/is-serializable-props.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/next/lib/is-serializable-props.ts b/packages/next/lib/is-serializable-props.ts index f09019dab2f91..d9bfcdc7a6785 100644 --- a/packages/next/lib/is-serializable-props.ts +++ b/packages/next/lib/is-serializable-props.ts @@ -1,7 +1,11 @@ const regexpPlainIdentifier = /^[A-Za-z_$][A-Za-z0-9_$]*$/ +function getObjectClassLabel(value: any): string { + return Object.prototype.toString.call(value) +} + function isPlainObject(value: any): boolean { - if (Object.prototype.toString.call(value) !== '[object Object]') { + if (getObjectClassLabel(value) !== '[object Object]') { return false } @@ -19,7 +23,7 @@ export function isSerializableProps( page, method, '', - `Props must be returned as a plain object from ${method}: \`{ props: { ... } }\`.` + `Props must be returned as a plain object from ${method}: \`{ props: { ... } }\` (received: \`${getObjectClassLabel(input)}\`).` ) } From e8ff41cc8d5ff1671053ccf876197b2208846f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20M=C3=BCller?= Date: Mon, 15 Mar 2021 11:23:18 +0100 Subject: [PATCH 2/6] Fixup unit tests for isSerializableProps --- test/unit/is-serializable-props.unit.test.js | 28 +++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/test/unit/is-serializable-props.unit.test.js b/test/unit/is-serializable-props.unit.test.js index d7f91138e4b2e..27291793282d0 100644 --- a/test/unit/is-serializable-props.unit.test.js +++ b/test/unit/is-serializable-props.unit.test.js @@ -6,14 +6,34 @@ describe('isSerializableProps', () => { expect(() => isSerializableProps('/', 'test', null)) .toThrowErrorMatchingInlineSnapshot(` "Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\`." -`) +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Null]\`)." + `) expect(() => isSerializableProps('/', 'test', undefined)) .toThrowErrorMatchingInlineSnapshot(` "Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\`." -`) +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Undefined]\`)." + `) + }) + + it('handles non-plain object props', () => { + expect(() => isSerializableProps('/', 'test', [])) + .toThrowErrorMatchingInlineSnapshot(` +"Error serializing props returned from \`test\` in \\"/\\". +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Array]\`)." + `) + + expect(() => isSerializableProps('/', 'test', class Foobar {})) + .toThrowErrorMatchingInlineSnapshot(` +"Error serializing props returned from \`test\` in \\"/\\". +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." + `) + + expect(() => isSerializableProps('/', 'test', function Foobar() {})) + .toThrowErrorMatchingInlineSnapshot(` +"Error serializing props returned from \`test\` in \\"/\\". +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." + `) }) it('allows empty props', () => { From 5340c017a2a4a73924569008beaae8059755e46d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20M=C3=BCller?= Date: Mon, 15 Mar 2021 11:46:19 +0100 Subject: [PATCH 3/6] Update snapshots --- test/unit/is-serializable-props.unit.test.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/unit/is-serializable-props.unit.test.js b/test/unit/is-serializable-props.unit.test.js index 27291793282d0..17ecf8725784b 100644 --- a/test/unit/is-serializable-props.unit.test.js +++ b/test/unit/is-serializable-props.unit.test.js @@ -5,34 +5,34 @@ describe('isSerializableProps', () => { it('handles null and undefined props', () => { expect(() => isSerializableProps('/', 'test', null)) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Null]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Null]\`)." `) expect(() => isSerializableProps('/', 'test', undefined)) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Undefined]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Undefined]\`)." `) }) it('handles non-plain object props', () => { expect(() => isSerializableProps('/', 'test', [])) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Array]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Array]\`)." `) expect(() => isSerializableProps('/', 'test', class Foobar {})) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." `) expect(() => isSerializableProps('/', 'test', function Foobar() {})) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." `) }) From e2f0e8018c66db401bb3cb5ababe90a4191f0f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Artur=20M=C3=BCller?= Date: Mon, 15 Mar 2021 11:55:25 +0100 Subject: [PATCH 4/6] Revert "Update snapshots" This reverts commit 5340c017a2a4a73924569008beaae8059755e46d. --- test/unit/is-serializable-props.unit.test.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/unit/is-serializable-props.unit.test.js b/test/unit/is-serializable-props.unit.test.js index 17ecf8725784b..27291793282d0 100644 --- a/test/unit/is-serializable-props.unit.test.js +++ b/test/unit/is-serializable-props.unit.test.js @@ -5,34 +5,34 @@ describe('isSerializableProps', () => { it('handles null and undefined props', () => { expect(() => isSerializableProps('/', 'test', null)) .toThrowErrorMatchingInlineSnapshot(` - "Error serializing props returned from \`test\` in \\"/\\". - Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Null]\`)." +"Error serializing props returned from \`test\` in \\"/\\". +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Null]\`)." `) expect(() => isSerializableProps('/', 'test', undefined)) .toThrowErrorMatchingInlineSnapshot(` - "Error serializing props returned from \`test\` in \\"/\\". - Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Undefined]\`)." +"Error serializing props returned from \`test\` in \\"/\\". +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Undefined]\`)." `) }) it('handles non-plain object props', () => { expect(() => isSerializableProps('/', 'test', [])) .toThrowErrorMatchingInlineSnapshot(` - "Error serializing props returned from \`test\` in \\"/\\". - Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Array]\`)." +"Error serializing props returned from \`test\` in \\"/\\". +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Array]\`)." `) expect(() => isSerializableProps('/', 'test', class Foobar {})) .toThrowErrorMatchingInlineSnapshot(` - "Error serializing props returned from \`test\` in \\"/\\". - Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." +"Error serializing props returned from \`test\` in \\"/\\". +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." `) expect(() => isSerializableProps('/', 'test', function Foobar() {})) .toThrowErrorMatchingInlineSnapshot(` - "Error serializing props returned from \`test\` in \\"/\\". - Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." +"Error serializing props returned from \`test\` in \\"/\\". +Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." `) }) From 5b2a269d19bb55ec81dcdfb0fbd82e5e74001905 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 25 Jul 2021 00:21:05 -0500 Subject: [PATCH 5/6] update snapshot --- test/unit/is-serializable-props.unit.test.js | 128 +++++++++---------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/test/unit/is-serializable-props.unit.test.js b/test/unit/is-serializable-props.unit.test.js index 27291793282d0..5cc96e66e3252 100644 --- a/test/unit/is-serializable-props.unit.test.js +++ b/test/unit/is-serializable-props.unit.test.js @@ -5,34 +5,34 @@ describe('isSerializableProps', () => { it('handles null and undefined props', () => { expect(() => isSerializableProps('/', 'test', null)) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Null]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Null]\`)." `) expect(() => isSerializableProps('/', 'test', undefined)) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Undefined]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Undefined]\`)." `) }) it('handles non-plain object props', () => { expect(() => isSerializableProps('/', 'test', [])) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Array]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Array]\`)." `) expect(() => isSerializableProps('/', 'test', class Foobar {})) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." `) expect(() => isSerializableProps('/', 'test', function Foobar() {})) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing props returned from \`test\` in \\"/\\". -Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." + "Error serializing props returned from \`test\` in \\"/\\". + Reason: Props must be returned as a plain object from test: \`{ props: { ... } }\` (received: \`[object Function]\`)." `) }) @@ -108,70 +108,70 @@ Reason: Props must be returned as a plain object from test: \`{ props: { ... } } it('disallows top-level non-serializable types', () => { expect(() => isSerializableProps('/', 'test', { toplevel: new Date() })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". -Reason: \`object\` (\\"[object Date]\\") cannot be serialized as JSON. Please only return JSON serializable data types." -`) + "Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". + Reason: \`object\` (\\"[object Date]\\") cannot be serialized as JSON. Please only return JSON serializable data types." + `) expect(() => isSerializableProps('/', 'test', { toplevel: class A {} })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". -Reason: \`function\` cannot be serialized as JSON. Please only return JSON serializable data types." -`) + "Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". + Reason: \`function\` cannot be serialized as JSON. Please only return JSON serializable data types." + `) expect(() => isSerializableProps('/', 'test', { toplevel: undefined })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". -Reason: \`undefined\` cannot be serialized as JSON. Please use \`null\` or omit this value." -`) + "Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". + Reason: \`undefined\` cannot be serialized as JSON. Please use \`null\` or omit this value." + `) expect(() => isSerializableProps('/', 'test', { toplevel: Symbol('FOOBAR') }) ).toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". -Reason: \`symbol\` cannot be serialized as JSON. Please only return JSON serializable data types." -`) + "Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". + Reason: \`symbol\` cannot be serialized as JSON. Please only return JSON serializable data types." + `) expect(() => isSerializableProps('/', 'test', { toplevel: function () {} })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". -Reason: \`function\` cannot be serialized as JSON. Please only return JSON serializable data types." -`) + "Error serializing \`.toplevel\` returned from \`test\` in \\"/\\". + Reason: \`function\` cannot be serialized as JSON. Please only return JSON serializable data types." + `) }) it('diallows nested non-serializable types', () => { expect(() => isSerializableProps('/', 'test', { k: { a: [1, { n: new Date() }] } }) ).toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k.a[1].n\` returned from \`test\` in \\"/\\". -Reason: \`object\` (\\"[object Date]\\") cannot be serialized as JSON. Please only return JSON serializable data types." -`) + "Error serializing \`.k.a[1].n\` returned from \`test\` in \\"/\\". + Reason: \`object\` (\\"[object Date]\\") cannot be serialized as JSON. Please only return JSON serializable data types." + `) expect(() => isSerializableProps('/', 'test', { k: { a: [1, { n: class A {} }] } }) ).toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k.a[1].n\` returned from \`test\` in \\"/\\". -Reason: \`function\` cannot be serialized as JSON. Please only return JSON serializable data types." -`) + "Error serializing \`.k.a[1].n\` returned from \`test\` in \\"/\\". + Reason: \`function\` cannot be serialized as JSON. Please only return JSON serializable data types." + `) expect(() => isSerializableProps('/', 'test', { k: { a: [1, undefined] } })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k.a[1]\` returned from \`test\` in \\"/\\". -Reason: \`undefined\` cannot be serialized as JSON. Please use \`null\` or omit this value." -`) + "Error serializing \`.k.a[1]\` returned from \`test\` in \\"/\\". + Reason: \`undefined\` cannot be serialized as JSON. Please use \`null\` or omit this value." + `) expect(() => isSerializableProps('/', 'test', { k: { n: Symbol('FOOBAR') } }) ).toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k.n\` returned from \`test\` in \\"/\\". -Reason: \`symbol\` cannot be serialized as JSON. Please only return JSON serializable data types." -`) + "Error serializing \`.k.n\` returned from \`test\` in \\"/\\". + Reason: \`symbol\` cannot be serialized as JSON. Please only return JSON serializable data types." + `) expect(() => isSerializableProps('/', 'test', { k: { a: [function () {}] } }) ).toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k.a[0]\` returned from \`test\` in \\"/\\". -Reason: \`function\` cannot be serialized as JSON. Please only return JSON serializable data types." -`) + "Error serializing \`.k.a[0]\` returned from \`test\` in \\"/\\". + Reason: \`function\` cannot be serialized as JSON. Please only return JSON serializable data types." + `) }) it('can handle obj circular refs', () => { @@ -180,15 +180,15 @@ Reason: \`function\` cannot be serialized as JSON. Please only return JSON seria expect(() => isSerializableProps('/', 'test', obj)) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.child\` returned from \`test\` in \\"/\\". -Reason: Circular references cannot be expressed in JSON (references: \`(self)\`)." -`) + "Error serializing \`.child\` returned from \`test\` in \\"/\\". + Reason: Circular references cannot be expressed in JSON (references: \`(self)\`)." + `) expect(() => isSerializableProps('/', 'test', { k: [obj] })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k[0].child\` returned from \`test\` in \\"/\\". -Reason: Circular references cannot be expressed in JSON (references: \`.k[0]\`)." -`) + "Error serializing \`.k[0].child\` returned from \`test\` in \\"/\\". + Reason: Circular references cannot be expressed in JSON (references: \`.k[0]\`)." + `) }) it('can handle arr circular refs', () => { @@ -197,15 +197,15 @@ Reason: Circular references cannot be expressed in JSON (references: \`.k[0]\`). expect(() => isSerializableProps('/', 'test', { arr })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.arr[2]\` returned from \`test\` in \\"/\\". -Reason: Circular references cannot be expressed in JSON (references: \`.arr\`)." -`) + "Error serializing \`.arr[2]\` returned from \`test\` in \\"/\\". + Reason: Circular references cannot be expressed in JSON (references: \`.arr\`)." + `) expect(() => isSerializableProps('/', 'test', { k: [{ arr }] })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k[0].arr[2]\` returned from \`test\` in \\"/\\". -Reason: Circular references cannot be expressed in JSON (references: \`.k[0].arr\`)." -`) + "Error serializing \`.k[0].arr[2]\` returned from \`test\` in \\"/\\". + Reason: Circular references cannot be expressed in JSON (references: \`.k[0].arr\`)." + `) }) it('can handle deep obj circular refs', () => { @@ -214,9 +214,9 @@ Reason: Circular references cannot be expressed in JSON (references: \`.k[0].arr expect(() => isSerializableProps('/', 'test', obj)) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.leve1.level2.child\` returned from \`test\` in \\"/\\". -Reason: Circular references cannot be expressed in JSON (references: \`(self)\`)." -`) + "Error serializing \`.leve1.level2.child\` returned from \`test\` in \\"/\\". + Reason: Circular references cannot be expressed in JSON (references: \`(self)\`)." + `) }) it('can handle deep obj circular refs (with arrays)', () => { @@ -225,9 +225,9 @@ Reason: Circular references cannot be expressed in JSON (references: \`(self)\`) expect(() => isSerializableProps('/', 'test', obj)) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.leve1.level2.child[0].another[0]\` returned from \`test\` in \\"/\\". -Reason: Circular references cannot be expressed in JSON (references: \`(self)\`)." -`) + "Error serializing \`.leve1.level2.child[0].another[0]\` returned from \`test\` in \\"/\\". + Reason: Circular references cannot be expressed in JSON (references: \`(self)\`)." + `) }) it('can handle deep arr circular refs', () => { @@ -236,9 +236,9 @@ Reason: Circular references cannot be expressed in JSON (references: \`(self)\`) expect(() => isSerializableProps('/', 'test', { k: arr })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k[3][1][2]\` returned from \`test\` in \\"/\\". -Reason: Circular references cannot be expressed in JSON (references: \`.k\`)." -`) + "Error serializing \`.k[3][1][2]\` returned from \`test\` in \\"/\\". + Reason: Circular references cannot be expressed in JSON (references: \`.k\`)." + `) }) it('can handle deep arr circular refs (with objects)', () => { @@ -247,9 +247,9 @@ Reason: Circular references cannot be expressed in JSON (references: \`.k\`)." expect(() => isSerializableProps('/', 'test', { k: arr })) .toThrowErrorMatchingInlineSnapshot(` -"Error serializing \`.k[3][1].nested[2]\` returned from \`test\` in \\"/\\". -Reason: Circular references cannot be expressed in JSON (references: \`.k\`)." -`) + "Error serializing \`.k[3][1].nested[2]\` returned from \`test\` in \\"/\\". + Reason: Circular references cannot be expressed in JSON (references: \`.k\`)." + `) }) it('allows multi object refs', () => { From d5d157b637f3e984ecc16f6b9a57d0da7483bc7a Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 25 Jul 2021 00:33:16 -0500 Subject: [PATCH 6/6] lint-fix --- packages/next/lib/is-serializable-props.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/next/lib/is-serializable-props.ts b/packages/next/lib/is-serializable-props.ts index d9bfcdc7a6785..058bfcd386450 100644 --- a/packages/next/lib/is-serializable-props.ts +++ b/packages/next/lib/is-serializable-props.ts @@ -23,7 +23,9 @@ export function isSerializableProps( page, method, '', - `Props must be returned as a plain object from ${method}: \`{ props: { ... } }\` (received: \`${getObjectClassLabel(input)}\`).` + `Props must be returned as a plain object from ${method}: \`{ props: { ... } }\` (received: \`${getObjectClassLabel( + input + )}\`).` ) }