Skip to content

Update creating addon libraries contributor docs #7800

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

Merged
merged 11 commits into from
May 14, 2025
Merged
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
2 changes: 2 additions & 0 deletions .github/workflows/release-workflow-v2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ jobs:
CI: true
- name: Run build
run: npm run build
- name: Generate types
run: npm run generate-types

# 2. Prepare release files
- run: mkdir release && mkdir p5 && cp -r ./lib/* p5/
Expand Down
258 changes: 138 additions & 120 deletions contributor_docs/creating_libraries.md

Large diffs are not rendered by default.

77 changes: 18 additions & 59 deletions docs/parameterData.json
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@
"image": {
"overloads": [
[
"p5.Image|p5.Element|p5.Texture|p5.Framebuffer|p5.FramebufferTexture",
"p5.Image|p5.Element|p5.Texture|p5.Framebuffer|p5.FramebufferTexture|p5.Renderer|p5.Graphics",
"Number",
"Number",
"Number?",
Expand Down Expand Up @@ -1427,14 +1427,7 @@
"createVector": {
"overloads": [
[
null
]
]
},
"createMatrix": {
"overloads": [
[
"Number[]"
"...Number[]"
]
]
},
Expand Down Expand Up @@ -1856,14 +1849,23 @@
},
"bezierOrder": {
"overloads": [
[],
[
"Number"
]
],
[]
]
},
"splineVertex": {
"overloads": [
[
"Number",
"Number"
],
[
"Number",
"Number",
"Number?"
],
[
"Number",
"Number",
Expand All @@ -1881,21 +1883,21 @@
},
"splineProperty": {
"overloads": [
[
"String"
],
[
"String",
null
],
[
"String"
]
]
},
"splineProperties": {
"overloads": [
[],
[
"Object"
]
],
[]
]
},
"vertex": {
Expand Down Expand Up @@ -1955,19 +1957,6 @@
]
]
},
"curveVertex": {
"overloads": [
[
"Number",
"Number"
],
[
"Number",
"Number",
"Number?"
]
]
},
"endShape": {
"overloads": [
[
Expand Down Expand Up @@ -2755,36 +2744,6 @@
]
]
},
"parseObj": {
"overloads": [
[]
]
},
"parseSTL": {
"overloads": [
[]
]
},
"isBinary": {
"overloads": [
[]
]
},
"matchDataViewAt": {
"overloads": [
[]
]
},
"parseBinarySTL": {
"overloads": [
[]
]
},
"parseASCIISTL": {
"overloads": [
[]
]
},
"model": {
"overloads": [
[
Expand Down
8 changes: 0 additions & 8 deletions package-lock.json

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

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@
"@vitest/browser": "^2.1.5",
"all-contributors-cli": "^6.19.0",
"concurrently": "^8.2.2",
"dayjs": "^1.11.10",
"documentation": "^14.0.3",
"eslint": "^8.54.0",
"glob": "^11.0.1",
Expand Down
5 changes: 2 additions & 3 deletions rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { string } from 'rollup-plugin-string';
import commonjs from '@rollup/plugin-commonjs';
import terser from '@rollup/plugin-terser';
import pkg from './package.json' with { type: 'json' };
import dayjs from 'dayjs';
import { visualizer } from 'rollup-plugin-visualizer';
import replace from '@rollup/plugin-replace';
import alias from '@rollup/plugin-alias';
Expand All @@ -27,7 +26,7 @@ const plugins = [
preventAssignment: true
})
];
const banner = `/*! p5.js v${pkg.version} ${dayjs().format('MMMM D, YYYY')} */`;
const banner = `/*! p5.js v${pkg.version} ${new Intl.DateTimeFormat('en-US', { month: 'long', day: 'numeric', year: 'numeric' }).format(new Date())} */`;
const bundleSize = (name, sourcemap) => {
return visualizer({
filename: `analyzer/${name}.html`,
Expand Down Expand Up @@ -80,7 +79,7 @@ const generateModuleBuild = () => {
plugins: [
...plugins
]
}
};
});
};

Expand Down
35 changes: 24 additions & 11 deletions src/core/friendly_errors/param_validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,14 @@ function validateParams(p5, fn, lifecycles) {
const isOptional = param?.endsWith('?');
param = param?.replace(/\?$/, '');

let schema = generateTypeSchema(param);
const isRest = param?.startsWith('...') && param?.endsWith('[]');
param = param?.replace(/^\.\.\.(.+)\[\]$/, '$1');

return isOptional ? schema.optional() : schema;
let schema = generateTypeSchema(param);
if (isOptional) {
schema = schema.optional();
}
return { schema, rest: isRest };
};

// Note that in Zod, `optional()` only checks for undefined, not the absence
Expand Down Expand Up @@ -262,14 +267,22 @@ function validateParams(p5, fn, lifecycles) {
const overloadSchemas = overloads.flatMap(overload => {
const combinations = generateOverloadCombinations(overload);

return combinations.map(combo =>
z.tuple(
combo
.map(p => generateParamSchema(p))
// For now, ignore schemas that cannot be mapped to a defined type
.filter(schema => schema !== undefined)
)
);
return combinations.map(combo => {
const params = combo
.map(p => generateParamSchema(p))
.filter(s => s.schema !== undefined);

let rest;
if (params.at(-1)?.rest) {
rest = params.pop();
}

let combined = z.tuple(params.map(s => s.schema));
if (rest) {
combined = combined.rest(rest.schema);
}
return combined;
});
});

return overloadSchemas.length === 1
Expand Down Expand Up @@ -504,7 +517,7 @@ function validateParams(p5, fn, lifecycles) {
// theoretically allowed to stay undefined and valid, it is likely that the
// user intended to call the function with non-undefined arguments. Skip
// regular workflow and return a friendly error message right away.
if (Array.isArray(args) && args.every(arg => arg === undefined)) {
if (Array.isArray(args) && args.length > 0 && args.every(arg => arg === undefined)) {
const undefinedErrorMessage = `🌸 p5.js says: All arguments for ${func}() are undefined. There is likely an error in the code.`;

return {
Expand Down
2 changes: 1 addition & 1 deletion src/image/loading_displaying.js
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ function loadingDisplaying(p5, fn){
* are set to `CENTER`.
*
* @method image
* @param {p5.Image|p5.Element|p5.Texture|p5.Framebuffer|p5.FramebufferTexture} img image to display.
* @param {p5.Image|p5.Element|p5.Texture|p5.Framebuffer|p5.FramebufferTexture|p5.Renderer|p5.Graphics} img image to display.
* @param {Number} x x-coordinate of the top-left corner of the image.
* @param {Number} y y-coordinate of the top-left corner of the image.
* @param {Number} [width] width to draw the image.
Expand Down
2 changes: 1 addition & 1 deletion src/math/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function math(p5, fn) {
* <a href="#/p5.Vector">p5.Vector</a> class.
*
* @method createVector
* @param {...Number} components Components of the vector.
* @param {...Number} x Zero or more numbers, representing each component of the vector.
* @return {p5.Vector} new <a href="#/p5.Vector">p5.Vector</a> object.
*
* @example
Expand Down
4 changes: 2 additions & 2 deletions src/webgl/light.js
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ function light(p5, fn){
* three parameters, `v1`, `v2`, and `v3`, set the light’s color using the
* current <a href="#/p5/colorMode">colorMode()</a>. The last parameter,
* `direction` sets the light’s direction using a
* <a href="#/p5.Geometry">p5.Geometry</a> object. For example,
* <a href="#/p5.Vector">p5.Vector</a> object. For example,
* `directionalLight(255, 0, 0, lightDir)` creates a red `(255, 0, 0)` light
* that shines in the direction the `lightDir` vector points.
*
Expand All @@ -488,7 +488,7 @@ function light(p5, fn){
* parameter, `color`, sets the light’s color using a
* <a href="#/p5.Color">p5.Color</a> object or an array of color values. The
* second parameter, `direction`, sets the light’s direction using a
* <a href="#/p5.Color">p5.Color</a> object. For example,
* <a href="#/p5.Vector">p5.Vector</a> object. For example,
* `directionalLight(myColor, lightDir)` creates a light that shines in the
* direction the `lightDir` vector points with the color value of `myColor`.
*
Expand Down
35 changes: 32 additions & 3 deletions test/unit/core/param_errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ suite('Validate Params', function () {
FramebufferTexture: function() {
return 'mock p5.FramebufferTexture';
},
Renderer: function() {
return 'mock p5.Renderer';
},
Graphics: function() {
return 'mock p5.Graphics';
},
_error: () => {},
};
const mockP5Prototype = {};
Expand Down Expand Up @@ -124,7 +130,7 @@ suite('Validate Params', function () {
console.log(result);
assert.equal(
result.error,
'🌸 p5.js says: Did you mean to put `await` before a loading function? An unexpected Promise was found. Expected Image or Element or Texture or Framebuffer or FramebufferTexture at the first parameter in p5.image().'
'🌸 p5.js says: Did you mean to put `await` before a loading function? An unexpected Promise was found. Expected Image or Element or Texture or Framebuffer or FramebufferTexture or Renderer or Graphics at the first parameter in p5.image().'
);
});
});
Expand Down Expand Up @@ -247,6 +253,29 @@ suite('Validate Params', function () {
];
const result = mockP5Prototype.validate('p5.paletteLerp', [colorStops, 0.5]);
assert.isTrue(result.success);
})
})
});
});

suite('validateParams: rest arguments', function () {
test('createVector(): works with no args', function() {
const result = mockP5Prototype.validate('p5.createVector', []);
assert.isTrue(result.success);
});
test('createVector(): works with one number', function() {
const result = mockP5Prototype.validate('p5.createVector', [1]);
assert.isTrue(result.success);
});
test('createVector(): works with many numbers', function() {
const result = mockP5Prototype.validate('p5.createVector', [1, 2, 3, 4]);
assert.isTrue(result.success);
});
test('createVector(): fails with a non-number', function() {
const result = mockP5Prototype.validate('p5.createVector', ['1']);
assert.isFalse(result.success);
});
test('createVector(): fails with any non-number', function() {
const result = mockP5Prototype.validate('p5.createVector', [1, 2, '3', 4]);
assert.isFalse(result.success);
});
});
});
9 changes: 7 additions & 2 deletions utils/convert.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ function typeObject(node) {
return { type: signature };
} else if (node.type === 'ArrayType') {
return { type: `[${node.elements.map(e => typeObject(e).type).join(', ')}]` };
} else if (node.type === 'RestType') {
return { type: typeObject(node.expression).type, rest: true };
} else {
// TODO
// - handle record types
Expand Down Expand Up @@ -518,19 +520,22 @@ function cleanUpClassItems(data) {

const processOverload = overload => {
if (overload.params) {
return Object.values(overload.params).map(param => processOptionalParam(param));
return Object.values(overload.params).map(param => processParam(param));
}
return overload;
}

// To simplify `parameterData.json`, instead of having a separate field for
// optional parameters, we'll add a ? to the end of parameter type to
// indicate that it's optional.
const processOptionalParam = param => {
const processParam = param => {
let type = param.type;
if (param.optional) {
type += '?';
}
if (param.rest) {
type = `...${type}[]`;
}
return type;
}

Expand Down
Loading