diff --git a/docs/parameterData.json b/docs/parameterData.json index b99217ebe8..de8cb04fa9 100644 --- a/docs/parameterData.json +++ b/docs/parameterData.json @@ -475,7 +475,7 @@ "applyMatrix": { "overloads": [ [ - "Array" + "Number[]" ], [ "Number", @@ -1300,9 +1300,6 @@ "Number", "Number", "Number" - ], - [ - "p5.Vector" ] ] }, @@ -2602,7 +2599,7 @@ "imageLight": { "overloads": [ [ - "p5.image" + "p5.Image" ] ] }, @@ -3052,7 +3049,6 @@ }, "createAudio": { "overloads": [ - [], [ "String|String[]?", "Function?" @@ -4128,6 +4124,9 @@ }, "mult": { "overloads": [ + [ + "Number" + ], [ "Number", "Number", @@ -4236,6 +4235,18 @@ ] ] }, + "dist": { + "overloads": [ + [ + "p5.Vector" + ], + [], + [ + "p5.Vector", + "p5.Vector" + ] + ] + }, "normalize": { "overloads": [ [], @@ -4385,6 +4396,11 @@ ] ] }, + "clampToZero": { + "overloads": [ + [] + ] + }, "fromAngle": { "overloads": [ [ @@ -4411,15 +4427,6 @@ "overloads": [ [] ] - }, - "dist": { - "overloads": [ - [], - [ - "p5.Vector", - "p5.Vector" - ] - ] } }, "p5.Font": { diff --git a/package.json b/package.json index 3ee0dad7ee..90d523ad7c 100644 --- a/package.json +++ b/package.json @@ -68,8 +68,14 @@ "license": "LGPL-2.1", "browser": "./lib/p5.min.js", "exports": { - ".": "./dist/app.js", - "./core": "./dist/core/main.js", + ".": { + "types": "./types/p5.d.ts", + "default": "./dist/app.js" + }, + "./core": { + "types": "./types/core/main.d.ts", + "default": "./dist/core/main.js" + }, "./shape": "./dist/shape/index.js", "./accessibility": "./dist/accessibility/index.js", "./friendlyErrors": "./dist/core/friendlyErrors/index.js", diff --git a/src/core/constants.js b/src/core/constants.js index 942b48c9ad..3a03b62799 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -740,7 +740,7 @@ export const POINTS = 0x0000; */ export const LINES = 0x0001; /** - * @property {0x0003} LINE_STRIP + * @typedef {0x0003} LINE_STRIP * @property {LINE_STRIP} LINE_STRIP * @final */ diff --git a/src/core/friendly_errors/param_validator.js b/src/core/friendly_errors/param_validator.js index d4fc78830a..12d5d42dc5 100644 --- a/src/core/friendly_errors/param_validator.js +++ b/src/core/friendly_errors/param_validator.js @@ -137,6 +137,7 @@ function validateParams(p5, fn, lifecycles) { * parameters, and `?` is a shorthand for `Optional`. * * @method generateZodSchemasForFunc + * @private * @param {String} func - Name of the function. Expect global functions like `sin` and class methods like `p5.Vector.add` * @returns {z.ZodSchema} Zod schema */ diff --git a/src/core/transform.js b/src/core/transform.js index 5ba580999c..1499e4e19d 100644 --- a/src/core/transform.js +++ b/src/core/transform.js @@ -56,7 +56,7 @@ function transform(p5, fn){ * cause shapes to transform continuously. * * @method applyMatrix - * @param {Array} arr an array containing the elements of the transformation matrix. Its length should be either 6 (2D) or 16 (3D). + * @param {Array} arr an array containing the elements of the transformation matrix. Its length should be either 6 (2D) or 16 (3D). * @chainable * * @example diff --git a/src/dom/p5.MediaElement.js b/src/dom/p5.MediaElement.js index 019d8cb664..a309ce5e69 100644 --- a/src/dom/p5.MediaElement.js +++ b/src/dom/p5.MediaElement.js @@ -5,6 +5,19 @@ import { Element } from './p5.Element'; +/** + * @typedef {'video'} VIDEO + * @property {VIDEO} VIDEO + * @final + */ +export const VIDEO = 'video'; +/** + * @typedef {'audio'} AUDIO + * @property {AUDIO} AUDIO + * @final + */ +export const AUDIO = 'audio'; + class MediaElement extends Element { constructor(elt, pInst) { super(elt, pInst); @@ -1318,7 +1331,7 @@ function media(p5, fn){ return c; } - /** VIDEO STUFF **/ + /*** VIDEO STUFF ***/ // Helps perform similar tasks for media element methods. function createMedia(pInst, type, src, callback) { @@ -1457,10 +1470,10 @@ function media(p5, fn){ */ fn.createVideo = function (src, callback) { // p5._validateParameters('createVideo', arguments); - return createMedia(this, 'video', src, callback); + return createMedia(this, VIDEO, src, callback); }; - /** AUDIO STUFF **/ + /*** AUDIO STUFF ***/ /** * Creates a hidden `<audio>` element for simple audio playback. @@ -1504,14 +1517,14 @@ function media(p5, fn){ */ fn.createAudio = function (src, callback) { // p5._validateParameters('createAudio', arguments); - return createMedia(this, 'audio', src, callback); + return createMedia(this, AUDIO, src, callback); }; - /** CAMERA STUFF **/ + /*** CAMERA STUFF ***/ - fn.VIDEO = 'video'; + fn.VIDEO = VIDEO; - fn.AUDIO = 'audio'; + fn.AUDIO = AUDIO; // from: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia // Older browsers might not implement mediaDevices at all, so we set an empty object first @@ -1693,7 +1706,7 @@ function media(p5, fn){ const videoConstraints = { video: useVideo, audio: useAudio }; constraints = Object.assign({}, videoConstraints, constraints); - const domElement = document.createElement('video'); + const domElement = document.createElement(VIDEO); // required to work in iOS 11 & up: domElement.setAttribute('playsinline', ''); navigator.mediaDevices.getUserMedia(constraints).then(function (stream) { diff --git a/src/math/p5.Vector.js b/src/math/p5.Vector.js index 97b72ab61b..3ce3d18b16 100644 --- a/src/math/p5.Vector.js +++ b/src/math/p5.Vector.js @@ -864,7 +864,6 @@ class Vector { * p5.Vector object and doesn't change the * originals. * - * @method mult * @param {Number} n The number to multiply with the vector * @chainable * @example @@ -1591,7 +1590,6 @@ class Vector { * Use dist() to calculate the distance between points * using coordinates as in `dist(x1, y1, x2, y2)`. * - * @method dist * @submodule p5.Vector * @param {p5.Vector} v x, y, and z coordinates of a p5.Vector. * @return {Number} distance. @@ -3060,7 +3058,6 @@ class Vector { * * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON * - * @method clampToZero * @return {p5.Vector} with components very close to zero replaced with zero. * @chainable */ diff --git a/src/webgl/light.js b/src/webgl/light.js index 815dfec4d9..378c9a88b5 100644 --- a/src/webgl/light.js +++ b/src/webgl/light.js @@ -895,7 +895,7 @@ function light(p5, fn){ * use as the light source. * * @method imageLight - * @param {p5.image} img image to use as the light source. + * @param {p5.Image} img image to use as the light source. * * @example *
diff --git a/src/webgl/p5.Quat.js b/src/webgl/p5.Quat.js index 7ecb773bff..537417c874 100644 --- a/src/webgl/p5.Quat.js +++ b/src/webgl/p5.Quat.js @@ -15,7 +15,7 @@ class Quat { * Returns a Quaternion for the * axis angle representation of the rotation * - * @method fromAxisAngle + * @private * @param {Number} [angle] Angle with which the points needs to be rotated * @param {Number} [x] x component of the axis vector * @param {Number} [y] y component of the axis vector @@ -34,7 +34,7 @@ class Quat { /** * Multiplies a quaternion with other quaternion. - * @method mult + * @private * @param {p5.Quat} [quat] quaternion to multiply with the quaternion calling the method. * @chainable */ @@ -55,6 +55,7 @@ class Quat { * the multiplication can be simplified to the below formula. * This was taken from the below stackexchange link * https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion/50545#50545 + * @private * @param {p5.Vector} [p] vector to rotate on the axis quaternion */ rotateVector(p) { @@ -68,7 +69,7 @@ class Quat { * Rotates the Quaternion by the quaternion passed * which contains the axis of roation and angle of rotation * - * @method rotateBy + * @private * @param {p5.Quat} [axesQuat] axis quaternion which contains * the axis of rotation and angle of rotation * @chainable diff --git a/src/webgl/text.js b/src/webgl/text.js index 7418e6cc52..1cd2ec60fe 100644 --- a/src/webgl/text.js +++ b/src/webgl/text.js @@ -132,6 +132,7 @@ function text(p5, fn) { /** * @function setPixel + * @private * @param {Object} imageInfo * @param {Number} r * @param {Number} g @@ -230,6 +231,7 @@ function text(p5, fn) { /** * @function push + * @private * @param {Number[]} xs the x positions of points in the curve * @param {Number[]} ys the y positions of points in the curve * @param {Object} v the curve information @@ -242,6 +244,7 @@ function text(p5, fn) { /** * @function minMax + * @private * @param {Number[]} rg the list of values to compare * @param {Number} min the initial minimum value * @param {Number} max the initial maximum value @@ -291,6 +294,7 @@ function text(p5, fn) { /** * @function clamp + * @private * @param {Number} v the value to clamp * @param {Number} min the minimum value * @param {Number} max the maxmimum value @@ -305,6 +309,7 @@ function text(p5, fn) { /** * @function byte + * @private * @param {Number} v the value to scale * * converts a floating-point number in the range 0-1 to a byte 0-255 @@ -440,6 +445,7 @@ function text(p5, fn) { /** * @function cubicToQuadratics + * @private * @param {Number} x0 * @param {Number} y0 * @param {Number} cx0 @@ -508,6 +514,7 @@ function text(p5, fn) { /** * @function pushLine + * @private * @param {Number} x0 * @param {Number} y0 * @param {Number} x1 @@ -523,6 +530,7 @@ function text(p5, fn) { /** * @function samePoint + * @private * @param {Number} x0 * @param {Number} y0 * @param {Number} x1 @@ -608,9 +616,10 @@ function text(p5, fn) { /** * @function layout + * @private * @param {Number[][]} dim - * @param {ImageInfo[]} dimImageInfos - * @param {ImageInfo[]} cellImageInfos + * @param {ImageInfos} dimImageInfos + * @param {ImageInfos} cellImageInfos * @return {Object} * * lays out the curves in a dimension (row or col) into two diff --git a/utils/generate-types.mjs b/utils/generate-types.mjs index 51921bf040..722c0a559a 100644 --- a/utils/generate-types.mjs +++ b/utils/generate-types.mjs @@ -2,7 +2,8 @@ import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; import { - generateTypeDefinitions + generateTypeDefinitions, + normalizeIdentifier } from "./helper.mjs"; // Fix for __dirname equivalent in ES modules @@ -53,7 +54,7 @@ export function generateAllDeclarationFiles() { `${parsedPath.name}.d.ts` ); - const exportName = parsedPath.name.replace('.', '_'); + const exportName = normalizeIdentifier(parsedPath.name.replace('.', '_')); const contentWithExport = content + `export default function ${exportName}(p5: any, fn: any): void;\n`; fs.mkdirSync(path.dirname(dtsPath), { recursive: true }); @@ -68,6 +69,8 @@ export function generateAllDeclarationFiles() { // Add references to all other .d.ts files const dtsFiles = findDtsFiles(path.join(__dirname, '..')); for (const file of dtsFiles) { + if (file === 'p5.d.ts') + continue; p5Types += `/// \n`; } p5Types += '\n'; diff --git a/utils/helper.mjs b/utils/helper.mjs index 6c340bc1df..e6a0de64d5 100644 --- a/utils/helper.mjs +++ b/utils/helper.mjs @@ -51,10 +51,13 @@ function generateP5TypeDefinitions(organizedData) { if (constData.description) { output += ` /**\n * ${constData.description}\n */\n`; } - if (constData.kind === 'constant') { - output += ` readonly ${constData.name.toUpperCase()}: ${constData.type};\n\n`; - } else { - output += ` static ${constData.name}: ${constData.type};\n\n`; + output += ` readonly ${constData.name}: ${constData.typeName};\n\n`; + + if (constData.name === 'VERSION') { // #todo: hardcoded special case + if (constData.description) { + output += ` /**\n * ${constData.description}\n */\n`; + } + output += ` static readonly ${constData.name}: ${constData.typeName};\n\n`; } } }); @@ -123,13 +126,15 @@ function generateGlobalTypeDefinitions(organizedData) { if (constData.description) { output += ` /**\n${formatJSDocComment(constData.description, 2)}\n */\n`; } - output += ` const ${constData.name.toUpperCase()}: p5.${constData.name.toUpperCase()};\n\n`; + output += ` const ${constData.name}: ${constData.typeName};\n\n`; } }); output += ` interface Window {\n`; instanceItems.forEach(item => { + if (item !== instanceItems.find(x => x.name === item.name)) + return; if (item.kind === 'function') { output += ` ${item.name}: typeof ${item.name};\n`; } @@ -140,7 +145,7 @@ function generateGlobalTypeDefinitions(organizedData) { if (constData.description) { output += ` /**\n * ${constData.description}\n */\n`; } - output += ` readonly ${constData.name.toUpperCase()}: typeof ${constData.name.toUpperCase()};\n`; + output += ` readonly ${constData.name}: ${constData.typeName};\n`; } }); @@ -238,23 +243,27 @@ function generateDeclarationFile(items, organizedData) { } items.forEach(item => { - if (item.kind !== 'class' && (!item.memberof || item.memberof !== classDoc?.name)) { + if (item.kind !== 'class' && (!item.memberof || normalizeClassName(item.memberof) !== normalizeClassName(classDoc?.name))) { switch (item.kind) { case 'function': - output += generateFunctionDeclaration(item); + if (!item.name.startsWith("_")) + output += generateFunctionDeclaration(item); break; case 'constant': case 'typedef': const constData = organizedData.consts[item.name]; if (constData) { + if (constData.kind === 'typedef') { + if (constData.description) { + output += ` /**\n * ${constData.description}\n */\n`; + } + output += ` type ${constData.name} = ${constData.type};\n\n`; + } + if (constData.description) { output += ` /**\n * ${constData.description}\n */\n`; } - if (constData.kind === 'constant') { - output += ` const ${constData.name}: ${constData.type};\n\n`; - } else { - output += ` type ${constData.name} = ${constData.type};\n\n`; - } + output += ` const ${constData.name}: ${constData.typeName};\n\n`; } break; } @@ -266,6 +275,38 @@ function generateDeclarationFile(items, organizedData) { return output; } + // #todo: repeated in convert.mjs + function getParams(entry) { + // Documentation.js seems to try to grab params from the function itself in + // the code if we don't document all the parameters. This messes with our + // manually-documented overloads. Instead of using the provided entry.params + // array, we'll instead only rely on manually included @param tags. + // + // However, the tags don't include a tree-structured description field, and + // instead convert it to a string. We want a slightly different conversion to + // string, so we match these params to the Documentation.js-provided `params` + // array and grab the description from those. + return (entry.tags || []) + + // Filter out the nested parameters (eg. options.extrude), + // to be treated as part of parent parameters (eg. options) + // and not separate entries + .filter(t => t.title === 'param' && !t.name.includes('.')) + .map(node => { + const param = (entry.params || []).find(param => param.name === node.name); + return { + name: normalizeIdentifier(node.name), + description: extractDescription(param?.description || { + type: 'text', // 'html', + value: node.description + }), + type: node.type, // generateTypeFromTag(node), + optional: node.type?.type === 'OptionalType', + rest: node.type?.type === 'RestType', + }; + }); + } + export function organizeData(data) { const allData = getAllEntries(data); @@ -283,33 +324,35 @@ function generateDeclarationFile(items, organizedData) { organized.classes[className] = { name: entry.name, description: extractDescription(entry.description), - params: (entry.params || []).map(param => ({ - name: param.name, - type: generateTypeFromTag(param), - optional: param.type?.type === 'OptionalType' - })), + params: getParams(entry), module, submodule, extends: entry.tags?.find(tag => tag.title === 'extends')?.name || null }; break; case 'function': - case 'property': - const overloads = entry.overloads?.map(overload => ({ + case 'property': // #todo: unreachable + case 'member': + const overloads = entry.overloads?.map(overload => ({ // #todo: unreachable params: overload.params, returns: overload.returns, description: extractDescription(overload.description) })); organized.classitems.push({ - name: entry.name, + // #todo: assumes all members are accessors + memberName: entry.name, + name: entry.kind === 'member' + ? entry.returns?.length > 0 ? `get ${entry.name}` : `set ${entry.name}` + : entry.name, kind: entry.kind, + // #todo: method (and sometimes param) descriptions only present on first overload description: extractDescription(entry.description), - params: (entry.params || []).map(param => ({ - name: param.name, - type: generateTypeFromTag(param), - optional: param.type?.type === 'OptionalType' - })), - returnType: entry.returns?.[0] ? generateTypeFromTag(entry.returns[0]) : 'void', + params: getParams(entry), + returnType: entry.tags?.find(tag => tag.title === 'chainable') + ? 'this' + : entry.returns?.[0] + ? generateTypeFromTag(entry.returns[0]) + : 'void', module, submodule, class: className, @@ -318,11 +361,21 @@ function generateDeclarationFile(items, organizedData) { }); break; case 'constant': case 'typedef': + let type = generateTypeFromTag(entry); + if (type === 'any') { + // find fallback type from property + const property = entry.properties?.find(x => x.name === entry.name); + const type2 = generateTypeFromTag(property); + if (type2 !== entry.name) { + type = type2; + } + } organized.consts[entry.name] = { name: entry.name, kind: entry.kind, description: extractDescription(entry.description), - type: entry.kind === 'constant' ? `P5.${entry.name.toUpperCase()}` : (entry.type ? generateTypeFromTag(entry) : 'any'), + type, + typeName: entry.kind === 'typedef' ? `p5.${entry.name}` : type, module, submodule, class: forEntry || 'p5' @@ -360,7 +413,7 @@ export function generateTypeFromTag(param) { case 'NameExpression': return normalizeTypeName(param.type.name); case 'TypeApplication': { - const baseType = normalizeTypeName(param.type.expression.name); + const baseType = normalizeTypeName(param.type.expression.name, { inApplication: true }); if (baseType === 'Array') { const innerType = param.type.applications[0]; @@ -384,9 +437,13 @@ export function generateTypeFromTag(param) { return 'any'; case 'RecordType': return 'object'; + case 'NumericLiteralType': + return `${param.type.value}`; case 'StringLiteralType': return `'${param.type.value}'`; - case 'UndefinedLiteralType': + case 'NullLiteral': + return 'null'; + case 'UndefinedLiteral': return 'undefined'; case 'ArrayType': { const innerTypeStrs = param.type.elements.map(e => generateTypeFromTag({ type: e })); @@ -394,16 +451,36 @@ export function generateTypeFromTag(param) { } case 'RestType': return `${generateTypeFromTag({ type: param.type.expression })}[]`; + case 'FunctionType': + const params = (param.type.params || []) + .map((param2, i) => generateParamDeclaration({ name: `arg${i}`, type: param2 })) + .join(', '); + + const returnType = param.type.result + ? generateTypeFromTag({ type: param.type.result }) + : 'void'; + return `(${params}) => ${returnType}`; default: return 'any'; } } - export function normalizeTypeName(type) { + export function normalizeIdentifier(name) { + return ( + '0123456789'.includes(name[0]) || + name === 'class' + ) ? '$' + name : name; + } + + export function normalizeTypeName(type, { inApplication = false } = {}) { if (!type) return 'any'; if (type === '[object Object]') return 'any'; + const constData = organized.consts[type]; + if (constData) + return constData.typeName; + const primitiveTypes = { 'String': 'string', 'Number': 'number', @@ -411,7 +488,9 @@ export function generateTypeFromTag(param) { 'Boolean': 'boolean', 'Void': 'void', 'Object': 'object', - 'Array': 'Array', + 'Any': 'any', + 'Array': !inApplication && 'any[]', + 'Promise': !inApplication && 'Promise', 'Function': 'Function' }; @@ -421,9 +500,11 @@ export function generateTypeFromTag(param) { export function generateParamDeclaration(param) { if (!param) return 'any'; + const name = normalizeIdentifier(param.name); + let type = param.type; let prefix = ''; - const isOptional = param.type?.type === 'OptionalType'; + const isOptional = param.optional || param.type?.type === 'OptionalType'; if (typeof type === 'string') { type = normalizeTypeName(type); } else if (param.type?.type) { @@ -432,36 +513,39 @@ export function generateTypeFromTag(param) { type = 'any'; } - if (param.type?.type === 'RestType') { + if (param.rest || param.type?.type === 'RestType') { prefix = '...'; } - return `${prefix}${param.name}${isOptional ? '?' : ''}: ${type}`; + return `${prefix}${name}${isOptional ? '?' : ''}: ${type}`; } export function generateFunctionDeclaration(funcDoc) { let output = ''; - if (funcDoc.description || funcDoc.tags?.length > 0) { - output += '/**\n'; + let comment = ''; + if (funcDoc.description) { const description = extractDescription(funcDoc.description); if (description) { - output += formatJSDocComment(description) + '\n'; + comment += (comment === "") ? '/**\n' : ' *\n'; + comment += formatJSDocComment(description) + '\n'; } - if (funcDoc.tags) { - if (description) { - output += ' *\n'; + } + if (funcDoc.tags) { + comment += (comment === "") ? '/**\n' : ' *\n'; + funcDoc.tags.forEach(tag => { + if (tag.description) { + const tagDesc = extractDescription(tag.description); + comment += formatJSDocComment(`@${tag.title} ${tag.name ? tag.name + ' ' : ''}${tagDesc}`, 0) + '\n'; } - funcDoc.tags.forEach(tag => { - if (tag.description) { - const tagDesc = extractDescription(tag.description); - output += formatJSDocComment(`@${tag.title} ${tagDesc}`, 0) + '\n'; - } - }); - } - output += ' */\n'; + }); } + if (comment !== "") + comment += ' */\n'; + output += comment; + + const name = normalizeIdentifier(funcDoc.name); const params = (funcDoc.params || []) .map(param => generateParamDeclaration(param)) @@ -471,33 +555,40 @@ export function generateTypeFromTag(param) { ? generateTypeFromTag(funcDoc.returns[0]) : 'void'; - output += `function ${funcDoc.name}(${params}): ${returnType};\n\n`; + output += `function ${name}(${params}): ${returnType};\n\n`; return output; } export function generateMethodDeclarations(item, isStatic = false, isGlobal = false) { let output = ''; + let comment = ''; if (item.description) { - output += ' /**\n'; const itemDesc = extractDescription(item.description); - output += formatJSDocComment(itemDesc, 2) + '\n'; - if (item.params?.length > 0) { - output += ' *\n'; - item.params.forEach(param => { - const paramDesc = extractDescription(param.description); - output += formatJSDocComment(`@param ${paramDesc}`, 2) + '\n'; - }); + if (itemDesc) { + comment += (comment === "") ? ' /**\n' : ' *\n'; + comment += formatJSDocComment(itemDesc, 2) + '\n'; } - if (item.returns) { - output += ' *\n'; - const returnDesc = extractDescription(item.returns[0]?.description); - output += formatJSDocComment(`@return ${returnDesc}`, 2) + '\n'; + } + if (item.params?.length > 0) { + comment += (comment === "") ? ' /**\n' : ' *\n'; + item.params.forEach(param => { + const paramDesc = extractDescription(param.description); + comment += formatJSDocComment(`@param ${param.name} ${paramDesc}`, 2) + '\n'; + }); + } + if (item.returns) { + const returnDesc = extractDescription(item.returns[0]?.description); + if (returnDesc) { + comment += (comment === "") ? ' /**\n' : ' *\n'; + comment += formatJSDocComment(`@return ${returnDesc}`, 2) + '\n'; } - output += ' */\n'; } + if (comment !== "") + comment += ' */\n'; + output += comment; - if (item.kind === 'function') { + if (item.kind === 'function' || item.kind === 'member') { const staticPrefix = isStatic ? 'static ' : ''; if (item.overloads?.length > 0) { @@ -515,7 +606,11 @@ export function generateTypeFromTag(param) { const params = (item.params || []) .map(param => generateParamDeclaration(param)) .join(', '); - output += ` ${staticPrefix}${item.name}(${params}): ${item.returnType};\n\n`; + if (item.kind === 'member' && item.name.startsWith('set ')) { + output += ` ${staticPrefix}${item.name}(${params});\n\n`; // return type annotation illegal + } else { + output += ` ${staticPrefix}${item.name}(${params}): ${item.returnType};\n\n`; + } } else { const staticPrefix = isStatic ? 'static ' : ''; output += ` ${staticPrefix}${item.name}: ${item.returnType};\n\n`; diff --git a/utils/patch.mjs b/utils/patch.mjs index d70f983006..e3c9ed9452 100644 --- a/utils/patch.mjs +++ b/utils/patch.mjs @@ -1,41 +1,79 @@ import fs from 'fs'; +const cache = {}; +const patched = {}; const replace = (path, src, dest) => { - try { - const data = fs - .readFileSync(path, { encoding: 'utf-8' }) - .replace(src, dest); - fs.writeFileSync(path, data); - } catch (err) { - console.error(err); - } + if (Array.isArray(path)) { + path.forEach(path => replace(path, src, dest)); + return; + } + try { + if (!path.startsWith("types/")) + path = "types/" + path; + + const before = patched[path] ?? + (cache[path] ??= fs.readFileSync("./" + path, { encoding: 'utf-8' })); + const after = before.replace(src, dest); + + if (after !== before) + patched[path] = after; + else + console.error(`A patch failed in ${path}:\n -${src}\n +${dest}`); + } catch (err) { + console.error(err); + } }; +// #todo: p5 function doc in structure.d.ts should be merged into the p5 constructor, which then needs to be written to parameterData +replace( + "global.d.ts", + `function p5(sketch: object, node: string | HTMLElement): void;`, + `// function p5(sketch: object, node: string | HTMLElement): void;`, +); +replace( + "global.d.ts", + `p5: typeof p5;`, + `// p5: typeof p5;`, +); +replace( + "p5.d.ts", + "p5(sketch: object, node: string | HTMLElement): void;", + "// p5(sketch: object, node: string | HTMLElement): void;" +); replace( - "./types/core/structure.d.ts", - "function p5(sketch: object, node: string | HTMLElement): void;", - "function p5: typeof p5" + "core/structure.d.ts", + "function p5(sketch: object, node: string | HTMLElement): void;", + "// function p5: typeof p5" ); replace( - "./types/webgl/p5.Geometry.d.ts", - "constructor(detailX?: number, detailY?: number, callback?: function);", - `constructor( - detailX?: number, - detailY?: number, - callback?: (this: { - detailY: number, - detailX: number, - vertices: p5.Vector[], - uvs: number[] - }) => void);` + "webgl/p5.Geometry.d.ts", + "constructor(detailX?: number, detailY?: number, callback?: function);", + `constructor( + detailX?: number, + detailY?: number, + callback?: (this: { + detailY: number, + detailX: number, + vertices: p5.Vector[], + uvs: number[] + }) => void);` ); // https://github.com/p5-types/p5.ts/issues/31 +// #todo: add readonly to appropriate array params, either here or in doc comments replace( - "./types/math/random.d.ts", - "function random(choices: Array): any;", - "function random(choices: T[]): T;" + [ "p5.d.ts", "math/random.d.ts" ], + "random(choices: any[]): any;", + "random(choices: readonly T[]): T;" ); +for (const [path, data] of Object.entries(patched)) { + try { + console.log(`Patched ${path}`); + fs.writeFileSync("./" + path, data); + } catch (err) { + console.error(err); + } +}