From ad1f355e7adffeab608a654f0e4cdff8da4c1b4e Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Thu, 9 Apr 2026 16:42:14 +0100 Subject: [PATCH 01/21] Move FES module out of core --- src/app.js | 3 +-- src/{core => }/friendly_errors/browser_errors.js | 0 src/{core => }/friendly_errors/fes_core.js | 4 ++-- src/{core => }/friendly_errors/file_errors.js | 2 +- src/{core => }/friendly_errors/index.js | 0 src/{core => }/friendly_errors/param_validator.js | 4 ++-- src/{core => }/friendly_errors/sketch_verifier.js | 2 +- src/{core => }/friendly_errors/stacktrace.js | 0 8 files changed, 7 insertions(+), 8 deletions(-) rename src/{core => }/friendly_errors/browser_errors.js (100%) rename src/{core => }/friendly_errors/fes_core.js (99%) rename src/{core => }/friendly_errors/file_errors.js (97%) rename src/{core => }/friendly_errors/index.js (100%) rename src/{core => }/friendly_errors/param_validator.js (99%) rename src/{core => }/friendly_errors/sketch_verifier.js (99%) rename src/{core => }/friendly_errors/stacktrace.js (100%) diff --git a/src/app.js b/src/app.js index b0fc757d67..f422305a87 100644 --- a/src/app.js +++ b/src/app.js @@ -15,7 +15,7 @@ color(p5); // core // currently, it only contains the test for parameter validation -import friendlyErrors from './core/friendly_errors'; +import friendlyErrors from './friendly_errors'; friendlyErrors(p5); // data @@ -64,4 +64,3 @@ import { waitForDocumentReady, waitingForTranslator, _globalInit } from './core/ Promise.all([waitForDocumentReady(), waitingForTranslator]).then(_globalInit); export default p5; - diff --git a/src/core/friendly_errors/browser_errors.js b/src/friendly_errors/browser_errors.js similarity index 100% rename from src/core/friendly_errors/browser_errors.js rename to src/friendly_errors/browser_errors.js diff --git a/src/core/friendly_errors/fes_core.js b/src/friendly_errors/fes_core.js similarity index 99% rename from src/core/friendly_errors/fes_core.js rename to src/friendly_errors/fes_core.js index 8962745918..2d1474cc21 100644 --- a/src/core/friendly_errors/fes_core.js +++ b/src/friendly_errors/fes_core.js @@ -22,9 +22,9 @@ * sequence of each function, please look at the FES Reference + Dev Notes: * https://github.com/processing/p5.js/blob/main/contributor_docs/fes_reference_dev_notes.md */ -import { translator } from '../internationalization'; +import { translator } from '../core/internationalization'; import errorTable from './browser_errors'; -import * as contants from '../constants'; +import * as contants from '../core/constants'; function fesCore(p5, fn){ // p5.js blue, p5.js orange, auto dark green; fallback p5.js darkened magenta diff --git a/src/core/friendly_errors/file_errors.js b/src/friendly_errors/file_errors.js similarity index 97% rename from src/core/friendly_errors/file_errors.js rename to src/friendly_errors/file_errors.js index 8f212c8355..8316074d32 100644 --- a/src/core/friendly_errors/file_errors.js +++ b/src/friendly_errors/file_errors.js @@ -2,7 +2,7 @@ * @for p5 * @requires core */ -import { translator } from '../internationalization'; +import { translator } from '../core/internationalization'; function fileErrors(p5, fn){ // mapping used by `_friendlyFileLoadError` diff --git a/src/core/friendly_errors/index.js b/src/friendly_errors/index.js similarity index 100% rename from src/core/friendly_errors/index.js rename to src/friendly_errors/index.js diff --git a/src/core/friendly_errors/param_validator.js b/src/friendly_errors/param_validator.js similarity index 99% rename from src/core/friendly_errors/param_validator.js rename to src/friendly_errors/param_validator.js index 5fa4e73151..f4d9a927f9 100644 --- a/src/core/friendly_errors/param_validator.js +++ b/src/friendly_errors/param_validator.js @@ -2,9 +2,9 @@ * @for p5 * @requires core */ -import * as constants from '../constants.js'; +import * as constants from '../core/constants.js'; import { z } from 'zod/v4'; -import dataDoc from '../../../docs/parameterData.json'; +import dataDoc from '../../docs/parameterData.json'; function validateParams(p5, fn, lifecycles) { // Cache for Zod schemas diff --git a/src/core/friendly_errors/sketch_verifier.js b/src/friendly_errors/sketch_verifier.js similarity index 99% rename from src/core/friendly_errors/sketch_verifier.js rename to src/friendly_errors/sketch_verifier.js index 4bbb7a274b..c9d5bbb317 100644 --- a/src/core/friendly_errors/sketch_verifier.js +++ b/src/friendly_errors/sketch_verifier.js @@ -1,6 +1,6 @@ import { parse } from 'acorn'; import { simple as walk } from 'acorn-walk'; -import * as constants from '../constants'; +import * as constants from '../core/constants'; // List of functions to ignore as they either are meant to be re-defined or // generate false positive outputs. diff --git a/src/core/friendly_errors/stacktrace.js b/src/friendly_errors/stacktrace.js similarity index 100% rename from src/core/friendly_errors/stacktrace.js rename to src/friendly_errors/stacktrace.js From 2866dbcb3f0384f8df5b78bccfe326b956e5264f Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Thu, 9 Apr 2026 23:14:01 +0100 Subject: [PATCH 02/21] Refactor and clean up --- src/core/internationalization.js | 2 +- src/friendly_errors/fes_core.js | 225 ++++++++++--------------- src/friendly_errors/sketch_verifier.js | 1 - 3 files changed, 88 insertions(+), 140 deletions(-) diff --git a/src/core/internationalization.js b/src/core/internationalization.js index b29ef72989..b20702e4e3 100644 --- a/src/core/internationalization.js +++ b/src/core/internationalization.js @@ -148,7 +148,7 @@ export const initialize = () => { }, backend: { fallback: 'en', - + // ensure that the FES internationalization strings are loaded // from the latest patch of the current minor version of p5.js loadPath: `https://cdn.jsdelivr.net/npm/p5@${ diff --git a/src/friendly_errors/fes_core.js b/src/friendly_errors/fes_core.js index 2d1474cc21..607c244e8f 100644 --- a/src/friendly_errors/fes_core.js +++ b/src/friendly_errors/fes_core.js @@ -24,11 +24,9 @@ */ import { translator } from '../core/internationalization'; import errorTable from './browser_errors'; -import * as contants from '../core/constants'; -function fesCore(p5, fn){ +function fesCore(p5, fn, lifecycles){ // p5.js blue, p5.js orange, auto dark green; fallback p5.js darkened magenta - // See testColors below for all the color codes and names const typeColors = ['#2D7BB6', '#EE9900', '#4DB200', '#C83C00']; let misusedAtTopLevelCode = null; let defineMisusedAtTopLevelCode = null; @@ -42,7 +40,30 @@ function fesCore(p5, fn){ // Used for internally thrown errors that should not get wrapped by another // friendly error handler - class FESError extends Error {}; + class FESError extends Error { }; + + lifecycles.presetup = function () { + let doFriendlyWelcome = false; // TEMP until we get it all working LM + if(doFriendlyWelcome){ + // p5.js brand - magenta: #ED225D + //const astrixBgColor = 'transparent'; + //const astrixTxtColor = '#ED225D'; + //const welcomeBgColor = '#ED225D'; + //const welcomeTextColor = 'white'; + const welcomeMessage = translator('fes.pre', { + message: translator('fes.welcome') + }); + console.log( + ' _ \n' + + ' /\\| |/\\ \n' + + " \\ ` ' / \n" + + ' / , . \\ \n' + + ' \\/|_|\\/ ' + + '\n\n' + + welcomeMessage + ); + } + }; if (typeof IS_MINIFIED !== 'undefined') { p5._friendlyError = @@ -50,10 +71,6 @@ function fesCore(p5, fn){ p5._fesErrorMonitor = () => {}; } else { - let doFriendlyWelcome = false; // TEMP until we get it all working LM - - // const errorTable = require('./browser_errors').default; - // -- Borrowed from jQuery 1.11.3 -- const class2type = {}; const toString = class2type.toString; @@ -105,26 +122,6 @@ function fesCore(p5, fn){ 'windowResized' ]; - const friendlyWelcome = () => { - // p5.js brand - magenta: #ED225D - //const astrixBgColor = 'transparent'; - //const astrixTxtColor = '#ED225D'; - //const welcomeBgColor = '#ED225D'; - //const welcomeTextColor = 'white'; - const welcomeMessage = translator('fes.pre', { - message: translator('fes.welcome') - }); - console.log( - ' _ \n' + - ' /\\| |/\\ \n' + - " \\ ` ' / \n" + - ' / , . \\ \n' + - ' \\/|_|\\/ ' + - '\n\n' + - welcomeMessage - ); - }; - /** * Takes a message and a p5 function func, and adds a link pointing to * the reference documentation of func at the end of the message @@ -149,7 +146,7 @@ function fesCore(p5, fn){ methodParts.length === 1 ? func : methodParts.slice(2).join('/'); //Whenever func having p5.[Class] is encountered, we need to have the error link as mentioned below else different link - funcName.startsWith('p5.') ? + funcName.startsWith('p5.') ? msgWithReference = `${message} (https://p5js.org/reference/${referenceSection}.${funcName})` : msgWithReference = `${message} (https://p5js.org/reference/${referenceSection}/${funcName})`; } @@ -169,15 +166,6 @@ function fesCore(p5, fn){ * @return console logs */ p5._report = (message, func, color) => { - // if p5._fesLogger is set ( i.e we are running tests ), use that - // instead of console.log - const log = - p5._fesLogger == null ? console.log.bind(console) : p5._fesLogger; - - if (doFriendlyWelcome) { - friendlyWelcome(); - doFriendlyWelcome = false; - } if ('undefined' === getType(color)) { color = '#B40033'; // dark magenta } else if (getType(color) === 'number') { @@ -191,9 +179,9 @@ function fesCore(p5, fn){ const prefixedMsg = translator('fes.pre', { message }); if (ENABLE_FES_STYLING) { - log('%c' + prefixedMsg, style.join(';')); + console.log('%c' + prefixedMsg, style.join(';')); } else { - log(prefixedMsg); + console.log(prefixedMsg); } }; @@ -227,7 +215,7 @@ function fesCore(p5, fn){ * @param {Number|String} [color] CSS color code */ p5._friendlyError = function(message, func, color) { - if (p5.disableFriendlyErrors) return; + // if (p5.disableFriendlyErrors) return; p5._report(message, func, color); }; @@ -246,59 +234,6 @@ function fesCore(p5, fn){ console.log(translator('fes.pre', { message })); }; - /** - * Measures dissimilarity between two strings by calculating - * the Levenshtein distance. - * - * If the "distance" between them is small enough, it is - * reasonable to think that one is the misspelled version of the other. - * - * Specifically, this uses the Wagner–Fischer algorithm. - * @method computeEditDistance - * @private - * @param {String} w1 the first word - * @param {String} w2 the second word - * - * @returns {Number} the "distance" between the two words, a smaller value - * indicates that the words are similar - */ - const computeEditDistance = (w1, w2) => { - const l1 = w1.length, - l2 = w2.length; - if (l1 === 0) return w2; - if (l2 === 0) return w1; - - let prev = []; - let cur = []; - - for (let j = 0; j < l2 + 1; j++) { - cur[j] = j; - } - - prev = cur; - - for (let i = 1; i < l1 + 1; i++) { - cur = []; - for (let j = 0; j < l2 + 1; j++) { - if (j === 0) { - cur[j] = i; - } else { - let a1 = w1[i - 1], - a2 = w2[j - 1]; - let temp = 999999; - let cost = a1.toLowerCase() === a2.toLowerCase() ? 0 : 1; - temp = temp > cost + prev[j - 1] ? cost + prev[j - 1] : temp; - temp = temp > 1 + cur[j - 1] ? 1 + cur[j - 1] : temp; - temp = temp > 1 + prev[j] ? 1 + prev[j] : temp; - cur[j] = temp; - } - } - prev = cur; - } - - return cur[l2]; - }; - /** * Whether or not p5.js is running in an environment where `preload` will be * run before `setup`. @@ -476,10 +411,6 @@ function fesCore(p5, fn){ * @param {Array} friendlyStack */ const printFriendlyStack = friendlyStack => { - const log = - p5._fesLogger && typeof p5._fesLogger === 'function' - ? p5._fesLogger - : console.log.bind(console); if (friendlyStack.length > 1) { let stacktraceMsg = ''; friendlyStack.forEach((frame, idx) => { @@ -500,7 +431,7 @@ function fesCore(p5, fn){ } stacktraceMsg += frameMsg; }); - log(stacktraceMsg); + console.log(stacktraceMsg); } }; @@ -967,38 +898,11 @@ function fesCore(p5, fn){ p5._fesErrorMonitor = fesErrorMonitor; p5._checkForUserDefinedFunctions = checkForUserDefinedFunctions; - - // logger for testing purposes. - p5._fesLogger = null; p5._fesLogCache = {}; window.addEventListener('load', checkForUserDefinedFunctions, false); window.addEventListener('error', p5._fesErrorMonitor, false); window.addEventListener('unhandledrejection', p5._fesErrorMonitor, false); - - /** - * Prints out all the colors in the color pallete with white text. - * For color blindness testing. - */ - /* function testColors() { - const str = 'A box of biscuits, a box of mixed biscuits and a biscuit mixer'; - p5._friendlyError(str, 'print', '#ED225D'); // p5.js magenta - p5._friendlyError(str, 'print', '#2D7BB6'); // p5.js blue - p5._friendlyError(str, 'print', '#EE9900'); // p5.js orange - p5._friendlyError(str, 'print', '#A67F59'); // p5.js light brown - p5._friendlyError(str, 'print', '#704F21'); // p5.js gold - p5._friendlyError(str, 'print', '#1CC581'); // auto cyan - p5._friendlyError(str, 'print', '#FF6625'); // auto orange - p5._friendlyError(str, 'print', '#79EB22'); // auto green - p5._friendlyError(str, 'print', '#B40033'); // p5.js darkened magenta - p5._friendlyError(str, 'print', '#084B7F'); // p5.js darkened blue - p5._friendlyError(str, 'print', '#945F00'); // p5.js darkened orange - p5._friendlyError(str, 'print', '#6B441D'); // p5.js darkened brown - p5._friendlyError(str, 'print', '#2E1B00'); // p5.js darkened gold - p5._friendlyError(str, 'print', '#008851'); // auto dark cyan - p5._friendlyError(str, 'print', '#C83C00'); // auto dark orange - p5._friendlyError(str, 'print', '#4DB200'); // auto dark green - } */ } // This is a lazily-defined list of p5 symbols that may be @@ -1019,7 +923,6 @@ function fesCore(p5, fn){ */ defineMisusedAtTopLevelCode = () => { const uniqueNamesFound = {}; - const getSymbols = obj => Object.getOwnPropertyNames(obj) .filter(name => { @@ -1048,13 +951,7 @@ function fesCore(p5, fn){ return { name, type }; }); - misusedAtTopLevelCode = [].concat( - getSymbols(fn), - // At present, p5 only adds its constants to fn during - // construction, which may not have happened at the time a - // ReferenceError is thrown, so we'll manually add them to our list. - getSymbols(contants) - ); + misusedAtTopLevelCode = getSymbols(fn); // This will ultimately ensure that we report the most specific error // possible to the user, e.g. advising them about HALF_PI instead of PI @@ -1131,9 +1028,6 @@ function fesCore(p5, fn){ }); }; - // Exposing this primarily for unit testing. - fn._helpForMisusedAtTopLevelCode = helpForMisusedAtTopLevelCode; - if (document.readyState !== 'complete') { window.addEventListener('error', helpForMisusedAtTopLevelCode, false); @@ -1150,5 +1044,60 @@ function fesCore(p5, fn){ export default fesCore; if (typeof p5 !== 'undefined') { - fesCore(p5, p5.prototype); + // fesCore(p5, p5.prototype); + p5.registerAddon(fesCore); } + +/** + * Measures dissimilarity between two strings by calculating + * the Levenshtein distance. + * + * If the "distance" between them is small enough, it is + * reasonable to think that one is the misspelled version of the other. + * + * Specifically, this uses the Wagner–Fischer algorithm. + * + * @method computeEditDistance + * @private + * @param {String} w1 the first word + * @param {String} w2 the second word + * + * @returns {Number} the "distance" between the two words, a smaller value + * indicates that the words are similar + */ +function computeEditDistance(w1, w2) { + const l1 = w1.length, + l2 = w2.length; + if (l1 === 0) return w2; + if (l2 === 0) return w1; + + let prev = []; + let cur = []; + + for (let j = 0; j < l2 + 1; j++) { + cur[j] = j; + } + + prev = cur; + + for (let i = 1; i < l1 + 1; i++) { + cur = []; + for (let j = 0; j < l2 + 1; j++) { + if (j === 0) { + cur[j] = i; + } else { + let a1 = w1[i - 1], + a2 = w2[j - 1]; + let temp = 999999; + let cost = a1.toLowerCase() === a2.toLowerCase() ? 0 : 1; + temp = temp > cost + prev[j - 1] ? cost + prev[j - 1] : temp; + temp = temp > 1 + cur[j - 1] ? 1 + cur[j - 1] : temp; + temp = temp > 1 + prev[j] ? 1 + prev[j] : temp; + cur[j] = temp; + } + } + prev = cur; + } + + return cur[l2]; +}; diff --git a/src/friendly_errors/sketch_verifier.js b/src/friendly_errors/sketch_verifier.js index c9d5bbb317..610df0455c 100644 --- a/src/friendly_errors/sketch_verifier.js +++ b/src/friendly_errors/sketch_verifier.js @@ -34,7 +34,6 @@ const ignoreFunction = [ ]; export const verifierUtils = { - /** * Fetches the contents of a script element in the user's sketch. * From f5b75951bdaf23cf1207271119efa49cf8112b75 Mon Sep 17 00:00:00 2001 From: limzykenneth Date: Sat, 25 Apr 2026 15:18:19 +0100 Subject: [PATCH 03/21] Work in progress proof of concept FES revamp using new translation utility --- package-lock.json | 7 + package.json | 3 +- preview/index.html | 237 +------------------------ src/friendly_errors/fes.js | 71 ++++++++ src/friendly_errors/file_errors.js | 2 + src/friendly_errors/param_validator.js | 53 +++--- 6 files changed, 114 insertions(+), 259 deletions(-) create mode 100644 src/friendly_errors/fes.js diff --git a/package-lock.json b/package-lock.json index 6ed36a0643..2c06778880 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "libtess": "^1.2.2", "omggif": "^1.0.10", "pako": "^2.1.0", + "tl-util": "https://pkg.pr.new/limzykenneth/tl-util@4881f69", "zod": "^4.2.1" }, "devDependencies": { @@ -12809,6 +12810,12 @@ "node": ">=14.0.0" } }, + "node_modules/tl-util": { + "version": "0.0.0", + "resolved": "https://pkg.pr.new/limzykenneth/tl-util@4881f69", + "integrity": "sha512-rrXSDGrct59fQiwOAnOz2zFlf9ePcFiH2qg9clER9UMl9TThBn5K93C74wIkMMonT7ACDoJeVaAcxhP5wxn5dw==", + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index 16f60cae3e..72ab2b1469 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "libtess": "^1.2.2", "omggif": "^1.0.10", "pako": "^2.1.0", - "zod": "^4.2.1" + "zod": "^4.2.1", + "tl-util": "https://pkg.pr.new/limzykenneth/tl-util@4881f69" }, "devDependencies": { "@eslint/compat": "^1.2.9", diff --git a/preview/index.html b/preview/index.html index 055f642f2e..6e74e1e38e 100644 --- a/preview/index.html +++ b/preview/index.html @@ -18,241 +18,18 @@