Skip to content

Update zod 3 to zod 4 support in p5.js dev-2.0 #7872

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: dev-2.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
"omggif": "^1.0.10",
"pako": "^2.1.0",
"pixelmatch": "^7.1.0",
"zod": "^3.23.8"
"zod": "^3.25.51"
},
"devDependencies": {
"@rollup/plugin-alias": "^5.1.1",
Expand Down
27 changes: 17 additions & 10 deletions src/core/friendly_errors/param_validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* @requires core
*/
import * as constants from '../constants.js';
import * as z from 'zod';
import { z } from 'zod/v4';
import dataDoc from '../../../docs/parameterData.json';

function validateParams(p5, fn, lifecycles) {
Expand Down Expand Up @@ -230,6 +230,10 @@ function validateParams(p5, fn, lifecycles) {
param = param?.replace(/^\.\.\.(.+)\[\]$/, '$1');

let schema = generateTypeSchema(param);
if (schema.def.type === 'function') {
schema = z.any()
}

if (isOptional) {
schema = schema.optional();
}
Expand Down Expand Up @@ -318,7 +322,7 @@ function validateParams(p5, fn, lifecycles) {
}

const numArgs = args.length;
const schemaItems = schema.items;
const schemaItems = schema.def.items;
const numSchemaItems = schemaItems.length;
const numRequiredSchemaItems = schemaItems.filter(item => !item.isOptional()).length;

Expand Down Expand Up @@ -353,11 +357,11 @@ function validateParams(p5, fn, lifecycles) {
};

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

const schemaUnion = schema._def.options;
const schemaUnion = schema.def.options;
schemaUnion.forEach(schema => {
const score = scoreSchema(schema);
if (score < bestScore) {
Expand Down Expand Up @@ -386,7 +390,7 @@ function validateParams(p5, fn, lifecycles) {
// (after scoring the schema closeness in `findClosestSchema`). Here, we
// always print the first error so that user can work through the errors
// one by one.
let currentError = zodErrorObj.errors[0];
let currentError = zodErrorObj.issues[0];

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

error.unionErrors.forEach(err => {
const issue = err.issues[0];
error.errors.forEach(err => {
const issue = err[0];
if (issue) {
if (!actualType) {
actualType = issue.received;
actualType = issue.message;
}

if (issue.code === 'invalid_type') {
actualType = issue.message.split(', received ')[1]
expectedTypes.add(issue.expected);
}
// 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_literal') {
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
}
}
});
Expand Down Expand Up @@ -452,7 +459,7 @@ function validateParams(p5, fn, lifecycles) {
break;
}
case 'invalid_type': {
message += buildTypeMismatchMessage(currentError.received, currentError.expected, currentError.path.join('.'));
message += buildTypeMismatchMessage(currentError.message.split(', received ')[1], currentError.expected, currentError.path.join('.'));
break;
}
case 'too_big': {
Expand Down
4 changes: 2 additions & 2 deletions test/unit/core/param_errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ suite('Validate Params', function () {
{ 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 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: '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 }) => {
Expand Down Expand Up @@ -278,4 +278,4 @@ suite('Validate Params', function () {
assert.isFalse(result.success);
});
});
});
});