Skip to content

Commit 9a595c3

Browse files
committed
Update exports and tests
1 parent 041aca0 commit 9a595c3

File tree

3 files changed

+136
-252
lines changed

3 files changed

+136
-252
lines changed

src/core/friendly_errors/friendly_errors_utils.js

Lines changed: 0 additions & 31 deletions
This file was deleted.
Lines changed: 96 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,49 @@
11
import { parse } from 'acorn';
22
import { simple as walk } from 'acorn-walk';
33
import * as constants from '../constants';
4-
import { loadP5Constructors } from './friendly_errors_utils';
54

6-
/**
7-
* @for p5
8-
* @requires core
9-
*/
10-
function sketchVerifier(p5, fn) {
11-
// List of functions to ignore as they either are meant to be re-defined or
12-
// generate false positive outputs.
13-
const ignoreFunction = [
14-
'setup',
15-
'draw',
16-
'preload',
17-
'deviceMoved',
18-
'deviceTurned',
19-
'deviceShaken',
20-
'doubleClicked',
21-
'mousePressed',
22-
'mouseReleased',
23-
'mouseMoved',
24-
'mouseDragged',
25-
'mouseClicked',
26-
'mouseWheel',
27-
'touchStarted',
28-
'touchMoved',
29-
'touchEnded',
30-
'keyPressed',
31-
'keyReleased',
32-
'keyTyped',
33-
'windowResized',
34-
'name',
35-
'parent',
36-
'toString',
37-
'print',
38-
'stop',
39-
'onended'
40-
];
5+
// List of functions to ignore as they either are meant to be re-defined or
6+
// generate false positive outputs.
7+
const ignoreFunction = [
8+
'setup',
9+
'draw',
10+
'preload',
11+
'deviceMoved',
12+
'deviceTurned',
13+
'deviceShaken',
14+
'doubleClicked',
15+
'mousePressed',
16+
'mouseReleased',
17+
'mouseMoved',
18+
'mouseDragged',
19+
'mouseClicked',
20+
'mouseWheel',
21+
'touchStarted',
22+
'touchMoved',
23+
'touchEnded',
24+
'keyPressed',
25+
'keyReleased',
26+
'keyTyped',
27+
'windowResized',
28+
// 'name',
29+
// 'parent',
30+
// 'toString',
31+
// 'print',
32+
// 'stop',
33+
// 'onended'
34+
];
35+
36+
export const verifierUtils = {
4137

4238
/**
4339
* Fetches the contents of a script element in the user's sketch.
44-
*
40+
*
41+
* @private
4542
* @method fetchScript
4643
* @param {HTMLScriptElement} script
4744
* @returns {Promise<string>}
48-
*/
49-
fn.fetchScript = async function (script) {
45+
*/
46+
fetchScript: async function (script) {
5047
if (script.src) {
5148
try {
5249
const contents = await fetch(script.src).then((res) => res.text());
@@ -59,37 +56,20 @@ function sketchVerifier(p5, fn) {
5956
} else {
6057
return script.textContent;
6158
}
62-
}
63-
64-
/**
65-
* Extracts the user's code from the script fetched. Note that this method
66-
* assumes that the user's code is always the last script element in the
67-
* sketch.
68-
*
69-
* @method getUserCode
70-
* @returns {Promise<string>} The user's code as a string.
71-
*/
72-
fn.getUserCode = async function () {
73-
// TODO: think of a more robust way to get the user's code. Refer to
74-
// https://github.com/processing/p5.js/pull/7293.
75-
const scripts = document.querySelectorAll('script');
76-
const userCodeScript = scripts[scripts.length - 1];
77-
const userCode = await fn.fetchScript(userCodeScript);
78-
79-
return userCode;
80-
}
59+
},
8160

8261
/**
8362
* Extracts the user-defined variables and functions from the user code with
8463
* the help of Espree parser.
85-
*
64+
*
65+
* @private
8666
* @method extractUserDefinedVariablesAndFuncs
8767
* @param {string} code - The code to extract variables and functions from.
8868
* @returns {Object} An object containing the user's defined variables and functions.
8969
* @returns {Array<{name: string, line: number}>} [userDefinitions.variables] Array of user-defined variable names and their line numbers.
9070
* @returns {Array<{name: string, line: number}>} [userDefinitions.functions] Array of user-defined function names and their line numbers.
9171
*/
92-
fn.extractUserDefinedVariablesAndFuncs = function (code) {
72+
extractUserDefinedVariablesAndFuncs: function (code) {
9373
const userDefinitions = {
9474
variables: [],
9575
functions: []
@@ -142,26 +122,27 @@ function sketchVerifier(p5, fn) {
142122
}
143123

144124
return userDefinitions;
145-
}
125+
},
146126

147127
/**
148128
* Checks user-defined variables and functions for conflicts with p5.js
149129
* constants and global functions.
150-
*
130+
*
151131
* This function performs two main checks:
152132
* 1. Verifies if any user definition conflicts with p5.js constants.
153133
* 2. Checks if any user definition conflicts with global functions from
154134
* p5.js renderer classes.
155-
*
135+
*
156136
* If a conflict is found, it reports a friendly error message and halts
157137
* further checking.
158-
*
138+
*
139+
* @private
159140
* @param {Object} userDefinitions - An object containing user-defined variables and functions.
160141
* @param {Array<{name: string, line: number}>} userDefinitions.variables - Array of user-defined variable names and their line numbers.
161142
* @param {Array<{name: string, line: number}>} userDefinitions.functions - Array of user-defined function names and their line numbers.
162143
* @returns {boolean} - Returns true if a conflict is found, false otherwise.
163144
*/
164-
fn.checkForConstsAndFuncs = function (userDefinitions, p5Constructors) {
145+
checkForConstsAndFuncs: function (userDefinitions, p5) {
165146
const allDefinitions = [
166147
...userDefinitions.variables,
167148
...userDefinitions.functions
@@ -172,78 +153,85 @@ function sketchVerifier(p5, fn) {
172153
// redefinition, the line number in user's code, and a link to its
173154
// reference on the p5.js website.
174155
function generateFriendlyError(errorType, name, line) {
175-
const url = `https://p5js.org/reference/#/p5/${name}`;
176-
const message = `${errorType} "${name}" on line ${line} is being redeclared and conflicts with a p5.js ${errorType.toLowerCase()}. JavaScript does not support declaring a ${errorType.toLowerCase()} more than once. p5.js reference: ${url}.`;
156+
const url = `https://p5js.org/reference/p5/${name}`;
157+
const message = `${errorType} "${name}" on line ${line} is being redeclared and conflicts with a p5.js ${errorType.toLowerCase()}. p5.js reference: ${url}`;
177158
return message;
178159
}
179160

180-
// Helper function that checks if a user definition has already been defined
181-
// in the p5.js library, either as a constant or as a function.
182-
function checkForRedefinition(name, libValue, line, type) {
183-
try {
184-
const userValue = eval("name");
185-
if (libValue !== userValue) {
186-
let message = generateFriendlyError(type, name, line);
187-
console.log(message);
188-
return true;
189-
}
190-
} catch (e) {
191-
// If eval fails, the function hasn't been redefined
192-
return false;
193-
}
194-
return false;
195-
}
196-
197161
// Checks for constant redefinitions.
198162
for (let { name, line } of allDefinitions) {
199163
const libDefinition = constants[name];
200164
if (libDefinition !== undefined) {
201-
if (checkForRedefinition(name, libDefinition, line, "Constant")) {
202-
return true;
203-
}
165+
const message = generateFriendlyError('Constant', name, line);
166+
console.log(message);
167+
return true;
204168
}
205169
}
206170

207171
// The new rules for attaching anything to global are (if true for both of
208172
// the following):
209-
// - It is a member of p5.prototype
173+
// - It is a member of p5.prototype
210174
// - Its name does not start with `_`
211175
const globalFunctions = new Set(
212-
Object.keys(p5.prototype).filter(key => !key.startsWith('_'))
176+
Object.getOwnPropertyNames(p5.prototype)
177+
.filter(key => !key.startsWith('_') && key !== 'constructor')
213178
);
214179

215180
for (let { name, line } of allDefinitions) {
216181
if (!ignoreFunction.includes(name) && globalFunctions.has(name)) {
217-
const prototypeFunc = p5.prototype[name];
218-
if (prototypeFunc && checkForRedefinition(name, prototypeFunc, line, "Function")) {
219-
return true;
220-
}
182+
const message = generateFriendlyError('Function', name, line);
183+
console.log(message);
184+
return true;
221185
}
222186
}
223187

224188
return false;
225-
}
189+
},
226190

227-
fn.run = async function () {
228-
const p5Constructors = await new Promise(resolve => {
229-
if (document.readyState === 'complete') {
230-
resolve(loadP5Constructors(p5));
231-
} else {
232-
window.addEventListener('load', () => resolve(loadP5Constructors(p5)));
233-
}
234-
});
191+
/**
192+
* Extracts the user's code from the script fetched. Note that this method
193+
* assumes that the user's code is always the last script element in the
194+
* sketch.
195+
*
196+
* @private
197+
* @method getUserCode
198+
* @returns {Promise<string>} The user's code as a string.
199+
*/
200+
getUserCode: async function () {
201+
// TODO: think of a more robust way to get the user's code. Refer to
202+
// https://github.com/processing/p5.js/pull/7293.
203+
const scripts = document.querySelectorAll('script');
204+
const userCodeScript = scripts[scripts.length - 1];
205+
const userCode = await verifierUtils.fetchScript(userCodeScript);
235206

236-
const userCode = await fn.getUserCode();
237-
const userDefinedVariablesAndFuncs = fn.extractUserDefinedVariablesAndFuncs(userCode);
207+
return userCode;
208+
},
238209

239-
if (fn.checkForConstsAndFuncs(userDefinedVariablesAndFuncs, p5Constructors)) {
240-
return;
241-
}
210+
/**
211+
* @private
212+
*/
213+
runFES: async function (p5) {
214+
const userCode = await verifierUtils.getUserCode();
215+
const userDefinedVariablesAndFuncs = verifierUtils.extractUserDefinedVariablesAndFuncs(userCode);
216+
217+
verifierUtils.checkForConstsAndFuncs(userDefinedVariablesAndFuncs, p5);
242218
}
219+
};
220+
221+
/**
222+
* @for p5
223+
* @requires core
224+
*/
225+
function sketchVerifier(p5, _fn, lifecycles) {
226+
lifecycles.presetup = async function() {
227+
if (!p5.disableFriendlyErrors) {
228+
verifierUtils.runFES(p5);
229+
}
230+
};
243231
}
244232

245233
export default sketchVerifier;
246234

247235
if (typeof p5 !== 'undefined') {
248236
sketchVerifier(p5, p5.prototype);
249-
}
237+
}

0 commit comments

Comments
 (0)