From c3ce8848c66c338d967e7ae0ed43bb10c5e5e687 Mon Sep 17 00:00:00 2001 From: Nitin Rajpoot Date: Sun, 2 Nov 2025 18:29:36 +0530 Subject: [PATCH 1/2] friendly-errors-fix --- src/core/friendly_errors/param_validator.js | 2 +- test/unit/core/param_errors.js | 32 ++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/core/friendly_errors/param_validator.js b/src/core/friendly_errors/param_validator.js index 4a6b206ae6..f20a06ede9 100644 --- a/src/core/friendly_errors/param_validator.js +++ b/src/core/friendly_errors/param_validator.js @@ -64,7 +64,7 @@ function validateParams(p5, fn, lifecycles) { 'Boolean': z.boolean(), 'Function': z.function(), 'Integer': z.number().int(), - 'Number': z.number(), + 'Number': z.union([z.number(), z.literal(Infinity), z.literal(-Infinity)]), 'Object': z.object({}), 'String': z.string() }; diff --git a/test/unit/core/param_errors.js b/test/unit/core/param_errors.js index e37d2c4d8e..6711949236 100644 --- a/test/unit/core/param_errors.js +++ b/test/unit/core/param_errors.js @@ -111,10 +111,10 @@ suite('Validate Params', function () { const invalidInputs = [ { name: 'missing required arc parameters #4, #5', input: [200, 100, 100, 80], msg: '🌸 p5.js says: Expected at least 6 arguments, but received fewer in p5.arc(). For more information, see https://p5js.org/reference/p5/arc.' }, - { name: 'missing required param #0', input: [undefined, 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: '🌸 p5.js says: Expected number at the first parameter, but received undefined in p5.arc().' }, - { name: 'missing required param #4', input: [200, 100, 100, 80, undefined, 0], msg: '🌸 p5.js says: Expected number at the fifth parameter, but received undefined in p5.arc().' }, - { name: 'missing optional param #5', input: [200, 100, 100, 80, 0, undefined, Math.PI], msg: '🌸 p5.js says: Expected number at the sixth parameter, but received undefined in p5.arc().' }, - { name: 'wrong param type at #0', input: ['a', 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: '🌸 p5.js says: Expected number at the first parameter, but received string in p5.arc().' } + { name: 'missing required param #0', input: [undefined, 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter in p5.arc().' }, + { name: 'missing required param #4', input: [200, 100, 100, 80, undefined, 0], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fifth parameter in p5.arc().' }, + { name: 'missing optional param #5', input: [200, 100, 100, 80, 0, undefined, Math.PI], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the sixth parameter in p5.arc().' }, + { name: 'wrong param type at #0', input: ['a', 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received a in p5.arc().' } ]; invalidInputs.forEach(({ name, input, msg }) => { @@ -145,13 +145,13 @@ suite('Validate Params', function () { suite('validateParams: a few edge cases', function () { const invalidInputs = [ - { fn: 'color', name: 'wrong type for optional parameter', input: [0, 0, 0, 'A'], msg: '🌸 p5.js says: Expected number at the fourth parameter, but received string in p5.color().' }, - { fn: 'color', name: 'superfluous parameter', input: [[0, 0, 0], 0], msg: '🌸 p5.js says: Expected number at the first parameter, but received array in p5.color().' }, - { fn: 'color', name: 'wrong element types', input: [['A', 'B', 'C']], msg: '🌸 p5.js says: Expected number at the first parameter, but received array in p5.color().' }, - { fn: 'rect', name: 'null, non-trailing, optional parameter', input: [0, 0, 0, 0, null, 0, 0, 0], msg: '🌸 p5.js says: Expected number at the fifth parameter, but received null in p5.rect().' }, + { fn: 'color', name: 'wrong type for optional parameter', input: [0, 0, 0, 'A'], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fourth parameter, but received A in p5.color().' }, + { fn: 'color', name: 'superfluous parameter', input: [[0, 0, 0], 0], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received 0,0,0 in p5.color().' }, + { fn: 'color', name: 'wrong element types', input: [['A', 'B', 'C']], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received A,B,C in p5.color().' }, + { fn: 'rect', name: 'null, non-trailing, optional parameter', input: [0, 0, 0, 0, null, 0, 0, 0], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fifth parameter in p5.rect().' }, { fn: 'color', name: 'too many args + wrong types too', input: ['A', 'A', 0, 0, 0, 0, 0, 0, 0, 0], msg: '🌸 p5.js says: Expected at most 4 arguments, but received more in p5.color(). For more information, see https://p5js.org/reference/p5/color.' }, - { fn: 'line', name: 'null string given', input: [1, 2, 4, 'null'], msg: '🌸 p5.js says: Expected number at the fourth parameter, but received string in p5.line().' }, - { fn: 'line', name: 'NaN value given', input: [1, 2, 4, NaN], msg: '🌸 p5.js says: Expected number at the fourth parameter, but received NaN in p5.line().' } + { fn: 'line', name: 'null string given', input: [1, 2, 4, 'null'], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fourth parameter, but received null in p5.line().' }, + { fn: 'line', name: 'NaN value given', input: [1, 2, 4, NaN], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fourth parameter in p5.line().' } ]; invalidInputs.forEach(({ name, input, fn, msg }) => { @@ -164,12 +164,12 @@ suite('Validate Params', function () { suite('validateParams: trailing undefined arguments', function () { const invalidInputs = [ - { fn: 'color', name: 'missing params #1, #2', input: [12, undefined, undefined], msg: '🌸 p5.js says: Expected number at the second parameter, but received undefined in p5.color().' }, + { fn: 'color', name: 'missing params #1, #2', input: [12, undefined, undefined], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the second parameter in p5.color().' }, // Even though the undefined arguments are technically allowed for // optional parameters, it is more likely that the user wanted to call // the function with meaningful arguments. { fn: 'random', name: 'missing params #0, #1', input: [undefined, undefined], msg: '🌸 p5.js says: All arguments for p5.random() are undefined. There is likely an error in the code.' }, - { fn: 'circle', name: 'missing compulsory parameter #2', input: [5, 5, undefined], msg: '🌸 p5.js says: Expected number at the third parameter, but received undefined in p5.circle().' } + { fn: 'circle', name: 'missing compulsory parameter #2', input: [5, 5, undefined], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the third parameter in p5.circle().' } ]; invalidInputs.forEach(({ fn, name, input, msg }) => { @@ -194,9 +194,9 @@ suite('Validate Params', function () { }); const invalidInputs = [ - { name: 'optional parameter, incorrect type', input: [65, 100, 100, 'a'], msg: '🌸 p5.js says: Expected number at the fourth parameter, but received string in p5.color().' }, - { name: 'extra parameter', input: [[65, 100, 100], 100], msg: '🌸 p5.js says: Expected number at the first parameter, but received array in p5.color().' }, - { name: 'incorrect element type', input: ['A', 'B', 'C'], msg: '🌸 p5.js says: Expected number at the first parameter, but received string in p5.color().' }, + { name: 'optional parameter, incorrect type', input: [65, 100, 100, 'a'], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fourth parameter, but received a in p5.color().' }, + { name: 'extra parameter', input: [[65, 100, 100], 100], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received 65,100,100 in p5.color().' }, + { name: 'incorrect element type', input: ['A', 'B', 'C'], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received A in p5.color().' }, { name: 'incorrect parameter count', input: ['A', 'A', 0, 0, 0, 0, 0, 0], msg: '🌸 p5.js says: Expected at most 4 arguments, but received more in p5.color(). For more information, see https://p5js.org/reference/p5/color.' } ]; @@ -224,7 +224,7 @@ suite('Validate Params', function () { test('set() with Boolean (invalid)', function () { const result = mockP5Prototype._validate('p5.set', [0, 0, true]); - assert.equal(result.error, '🌸 p5.js says: Expected number or array or object at the third parameter, but received boolean in p5.set().'); + assert.equal(result.error, '🌸 p5.js says: Expected array or object at the third parameter, but received boolean in p5.set().'); }); }); From d925a591204a9a5fce97e20a99a61d57ea5d8ccd Mon Sep 17 00:00:00 2001 From: Nitin Rajpoot Date: Wed, 5 Nov 2025 11:52:23 +0530 Subject: [PATCH 2/2] fix --- src/core/friendly_errors/param_validator.js | 60 +++++++++++++-------- test/unit/core/param_errors.js | 34 ++++++------ 2 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/core/friendly_errors/param_validator.js b/src/core/friendly_errors/param_validator.js index f20a06ede9..08156171ab 100644 --- a/src/core/friendly_errors/param_validator.js +++ b/src/core/friendly_errors/param_validator.js @@ -414,31 +414,47 @@ function validateParams(p5, fn, lifecycles) { const processUnionError = error => { const expectedTypes = new Set(); let actualType; + let isActualTypeSet = false; - error.errors.forEach(err => { - const issue = err[0]; - if (issue) { - if (!actualType) { - actualType = issue.message; - } + const setActualType = (val) => { + if (!isActualTypeSet) { + actualType = val; + isActualTypeSet = true; + } + }; - if (issue.code === 'invalid_type') { - actualType = issue.message.split(', received ')[1]; - expectedTypes.add(issue.expected); + const processIssues = (issues, path) => { + if (!issues) return; + issues.forEach(err => { + const issue = err[0]; + if (issue) { + if (issue.code === 'invalid_union') { + processIssues(issue.errors, issue.path); + } else { + if (issue.code === 'invalid_type') { + setActualType(issue.message.split(', received ')[1]); + expectedTypes.add(issue.expected); + } else if (issue.code === 'invalid_value') { + expectedTypes.add('constant (please refer to documentation for allowed values)'); + setActualType(args[path[0]]); + } else if (issue.code === 'custom') { + const match = issue.message.match(/Input not instance of (\w+)/); + if (match) expectedTypes.add(match[1]); + setActualType(undefined); + } + } } - // The case for constants. Since we don't want to print out the actual - // constant values in the error message, the error message will - // direct users to the documentation. - else if (issue.code === 'invalid_value') { - expectedTypes.add('constant (please refer to documentation for allowed values)'); - actualType = args[error.path[0]]; - } else if (issue.code === 'custom') { - const match = issue.message.match(/Input not instance of (\w+)/); - if (match) expectedTypes.add(match[1]); - actualType = undefined; - } - } - }); + }); + }; + + processIssues(error.errors, error.path); + + if ( + expectedTypes.has('number') && + expectedTypes.has('constant (please refer to documentation for allowed values)') + ) { + expectedTypes.delete('constant (please refer to documentation for allowed values)'); + } if (expectedTypes.size > 0) { if (error.path?.length > 0 && args[error.path[0]] instanceof Promise) { diff --git a/test/unit/core/param_errors.js b/test/unit/core/param_errors.js index 6711949236..7e96cedc5c 100644 --- a/test/unit/core/param_errors.js +++ b/test/unit/core/param_errors.js @@ -111,10 +111,10 @@ suite('Validate Params', function () { const invalidInputs = [ { name: 'missing required arc parameters #4, #5', input: [200, 100, 100, 80], msg: '🌸 p5.js says: Expected at least 6 arguments, but received fewer in p5.arc(). For more information, see https://p5js.org/reference/p5/arc.' }, - { name: 'missing required param #0', input: [undefined, 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter in p5.arc().' }, - { name: 'missing required param #4', input: [200, 100, 100, 80, undefined, 0], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fifth parameter in p5.arc().' }, - { name: 'missing optional param #5', input: [200, 100, 100, 80, 0, undefined, Math.PI], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the sixth parameter in p5.arc().' }, - { name: 'wrong param type at #0', input: ['a', 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received a in p5.arc().' } + { name: 'missing required param #0', input: [undefined, 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: '🌸 p5.js says: Expected number at the first parameter, but received undefined in p5.arc().' }, + { name: 'missing required param #4', input: [200, 100, 100, 80, undefined, 0], msg: '🌸 p5.js says: Expected number at the fifth parameter, but received undefined in p5.arc().' }, + { name: 'missing optional param #5', input: [200, 100, 100, 80, 0, undefined, Math.PI], msg: '🌸 p5.js says: Expected number at the sixth parameter, but received undefined in p5.arc().' }, + { name: 'wrong param type at #0', input: ['a', 100, 100, 80, 0, Math.PI, constants.PIE, 30], msg: '🌸 p5.js says: Expected number at the first parameter, but received string in p5.arc().' } ]; invalidInputs.forEach(({ name, input, msg }) => { @@ -145,13 +145,13 @@ suite('Validate Params', function () { suite('validateParams: a few edge cases', function () { const invalidInputs = [ - { fn: 'color', name: 'wrong type for optional parameter', input: [0, 0, 0, 'A'], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fourth parameter, but received A in p5.color().' }, - { fn: 'color', name: 'superfluous parameter', input: [[0, 0, 0], 0], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received 0,0,0 in p5.color().' }, - { fn: 'color', name: 'wrong element types', input: [['A', 'B', 'C']], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received A,B,C in p5.color().' }, - { fn: 'rect', name: 'null, non-trailing, optional parameter', input: [0, 0, 0, 0, null, 0, 0, 0], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fifth parameter in p5.rect().' }, + { fn: 'color', name: 'wrong type for optional parameter', input: [0, 0, 0, 'A'], msg: '🌸 p5.js says: Expected number at the fourth parameter, but received string in p5.color().' }, + { fn: 'color', name: 'superfluous parameter', input: [[0, 0, 0], 0], msg: '🌸 p5.js says: Expected number at the first parameter, but received array in p5.color().' }, + { fn: 'color', name: 'wrong element types', input: [['A', 'B', 'C']], msg: '🌸 p5.js says: Expected number at the first parameter, but received array in p5.color().' }, + { fn: 'rect', name: 'null, non-trailing, optional parameter', input: [0, 0, 0, 0, null, 0, 0, 0], msg: '🌸 p5.js says: Expected number at the fifth parameter, but received null in p5.rect().' }, { fn: 'color', name: 'too many args + wrong types too', input: ['A', 'A', 0, 0, 0, 0, 0, 0, 0, 0], msg: '🌸 p5.js says: Expected at most 4 arguments, but received more in p5.color(). For more information, see https://p5js.org/reference/p5/color.' }, - { fn: 'line', name: 'null string given', input: [1, 2, 4, 'null'], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fourth parameter, but received null in p5.line().' }, - { fn: 'line', name: 'NaN value given', input: [1, 2, 4, NaN], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fourth parameter in p5.line().' } + { fn: 'line', name: 'null string given', input: [1, 2, 4, 'null'], msg: '🌸 p5.js says: Expected number at the fourth parameter, but received string in p5.line().' }, + { fn: 'line', name: 'NaN value given', input: [1, 2, 4, NaN], msg: '🌸 p5.js says: Expected number at the fourth parameter, but received NaN in p5.line().' } ]; invalidInputs.forEach(({ name, input, fn, msg }) => { @@ -164,12 +164,12 @@ suite('Validate Params', function () { suite('validateParams: trailing undefined arguments', function () { const invalidInputs = [ - { fn: 'color', name: 'missing params #1, #2', input: [12, undefined, undefined], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the second parameter in p5.color().' }, + { fn: 'color', name: 'missing params #1, #2', input: [12, undefined, undefined], msg: '🌸 p5.js says: Expected number at the second parameter, but received undefined in p5.color().' }, // Even though the undefined arguments are technically allowed for // optional parameters, it is more likely that the user wanted to call // the function with meaningful arguments. { fn: 'random', name: 'missing params #0, #1', input: [undefined, undefined], msg: '🌸 p5.js says: All arguments for p5.random() are undefined. There is likely an error in the code.' }, - { fn: 'circle', name: 'missing compulsory parameter #2', input: [5, 5, undefined], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the third parameter in p5.circle().' } + { fn: 'circle', name: 'missing compulsory parameter #2', input: [5, 5, undefined], msg: '🌸 p5.js says: Expected number at the third parameter, but received undefined in p5.circle().' } ]; invalidInputs.forEach(({ fn, name, input, msg }) => { @@ -194,9 +194,9 @@ suite('Validate Params', function () { }); const invalidInputs = [ - { name: 'optional parameter, incorrect type', input: [65, 100, 100, 'a'], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the fourth parameter, but received a in p5.color().' }, - { name: 'extra parameter', input: [[65, 100, 100], 100], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received 65,100,100 in p5.color().' }, - { name: 'incorrect element type', input: ['A', 'B', 'C'], msg: '🌸 p5.js says: Expected number or constant (please refer to documentation for allowed values) at the first parameter, but received A in p5.color().' }, + { name: 'optional parameter, incorrect type', input: [65, 100, 100, 'a'], msg: '🌸 p5.js says: Expected number at the fourth parameter, but received string in p5.color().' }, + { name: 'extra parameter', input: [[65, 100, 100], 100], msg: '🌸 p5.js says: Expected number at the first parameter, but received array in p5.color().' }, + { name: 'incorrect element type', input: ['A', 'B', 'C'], msg: '🌸 p5.js says: Expected number at the first parameter, but received string in p5.color().' }, { name: 'incorrect parameter count', input: ['A', 'A', 0, 0, 0, 0, 0, 0], msg: '🌸 p5.js says: Expected at most 4 arguments, but received more in p5.color(). For more information, see https://p5js.org/reference/p5/color.' } ]; @@ -224,7 +224,7 @@ suite('Validate Params', function () { test('set() with Boolean (invalid)', function () { const result = mockP5Prototype._validate('p5.set', [0, 0, true]); - assert.equal(result.error, '🌸 p5.js says: Expected array or object at the third parameter, but received boolean in p5.set().'); + assert.equal(result.error, '🌸 p5.js says: Expected number or array or object at the third parameter, but received boolean in p5.set().'); }); }); @@ -279,4 +279,4 @@ suite('Validate Params', function () { assert.isFalse(result.success); }); }); -}); +}); \ No newline at end of file