Skip to content

Commit e24327d

Browse files
authored
Merge pull request #7872 from madhav2348/madhav/issue7833
Update zod 3 to zod 4 support in p5.js dev-2.0
2 parents 0da0430 + a97a789 commit e24327d

File tree

4 files changed

+26
-17
lines changed

4 files changed

+26
-17
lines changed

package-lock.json

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"omggif": "^1.0.10",
3535
"pako": "^2.1.0",
3636
"pixelmatch": "^7.1.0",
37-
"zod": "^3.23.8"
37+
"zod": "^3.25.51"
3838
},
3939
"devDependencies": {
4040
"@eslint/compat": "^1.2.9",

src/core/friendly_errors/param_validator.js

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @requires core
44
*/
55
import * as constants from '../constants.js';
6-
import * as z from 'zod';
6+
import { z } from 'zod/v4';
77
import dataDoc from '../../../docs/parameterData.json';
88

99
function validateParams(p5, fn, lifecycles) {
@@ -230,6 +230,12 @@ function validateParams(p5, fn, lifecycles) {
230230
param = param?.replace(/^\.\.\.(.+)\[\]$/, '$1');
231231

232232
let schema = generateTypeSchema(param);
233+
// Fallback to z.custom() because function types are no longer
234+
// returns a Zod schema.
235+
if (schema.def.type === 'function') {
236+
schema = z.custom(val => val instanceof Function)
237+
}
238+
233239
if (isOptional) {
234240
schema = schema.optional();
235241
}
@@ -318,7 +324,7 @@ function validateParams(p5, fn, lifecycles) {
318324
}
319325

320326
const numArgs = args.length;
321-
const schemaItems = schema.items;
327+
const schemaItems = schema.def.items;
322328
const numSchemaItems = schemaItems.length;
323329
const numRequiredSchemaItems = schemaItems.filter(item => !item.isOptional()).length;
324330

@@ -353,11 +359,11 @@ function validateParams(p5, fn, lifecycles) {
353359
};
354360

355361
// Default to the first schema, so that we are guaranteed to return a result.
356-
let closestSchema = schema._def.options[0];
362+
let closestSchema = schema.def.options[0];
357363
// We want to return the schema with the lowest score.
358364
let bestScore = Infinity;
359365

360-
const schemaUnion = schema._def.options;
366+
const schemaUnion = schema.def.options;
361367
schemaUnion.forEach(schema => {
362368
const score = scoreSchema(schema);
363369
if (score < bestScore) {
@@ -386,7 +392,7 @@ function validateParams(p5, fn, lifecycles) {
386392
// (after scoring the schema closeness in `findClosestSchema`). Here, we
387393
// always print the first error so that user can work through the errors
388394
// one by one.
389-
let currentError = zodErrorObj.errors[0];
395+
let currentError = zodErrorObj.issues[0];
390396

391397
// Helper function to build a type mismatch message.
392398
const buildTypeMismatchMessage = (actualType, expectedTypeStr, position) => {
@@ -403,24 +409,27 @@ function validateParams(p5, fn, lifecycles) {
403409
const expectedTypes = new Set();
404410
let actualType;
405411

406-
error.unionErrors.forEach(err => {
407-
const issue = err.issues[0];
412+
error.errors.forEach(err => {
413+
const issue = err[0];
408414
if (issue) {
409415
if (!actualType) {
410-
actualType = issue.received;
416+
actualType = issue.message;
411417
}
412418

413419
if (issue.code === 'invalid_type') {
420+
actualType = issue.message.split(', received ')[1]
414421
expectedTypes.add(issue.expected);
415422
}
416423
// The case for constants. Since we don't want to print out the actual
417424
// constant values in the error message, the error message will
418425
// direct users to the documentation.
419-
else if (issue.code === 'invalid_literal') {
426+
else if (issue.code === 'invalid_value') {
420427
expectedTypes.add("constant (please refer to documentation for allowed values)");
428+
actualType = args[error.path[0]];
421429
} else if (issue.code === 'custom') {
422430
const match = issue.message.match(/Input not instance of (\w+)/);
423431
if (match) expectedTypes.add(match[1]);
432+
actualType = undefined
424433
}
425434
}
426435
});
@@ -452,7 +461,7 @@ function validateParams(p5, fn, lifecycles) {
452461
break;
453462
}
454463
case 'invalid_type': {
455-
message += buildTypeMismatchMessage(currentError.received, currentError.expected, currentError.path.join('.'));
464+
message += buildTypeMismatchMessage(currentError.message.split(', received ')[1], currentError.expected, currentError.path.join('.'));
456465
break;
457466
}
458467
case 'too_big': {

test/unit/core/param_errors.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ suite('Validate Params', function () {
150150
{ 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().' },
151151
{ 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.' },
152152
{ 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().' },
153-
{ 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().' }
153+
{ 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().' }
154154
];
155155

156156
invalidInputs.forEach(({ name, input, fn, msg }) => {
@@ -278,4 +278,4 @@ suite('Validate Params', function () {
278278
assert.isFalse(result.success);
279279
});
280280
});
281-
});
281+
});

0 commit comments

Comments
 (0)