1
1
import { parse } from 'acorn' ;
2
2
import { simple as walk } from 'acorn-walk' ;
3
3
import * as constants from '../constants' ;
4
- import { loadP5Constructors } from './friendly_errors_utils' ;
5
4
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 = {
41
37
42
38
/**
43
39
* Fetches the contents of a script element in the user's sketch.
44
- *
40
+ *
41
+ * @private
45
42
* @method fetchScript
46
43
* @param {HTMLScriptElement } script
47
44
* @returns {Promise<string> }
48
- */
49
- fn . fetchScript = async function ( script ) {
45
+ */
46
+ fetchScript : async function ( script ) {
50
47
if ( script . src ) {
51
48
try {
52
49
const contents = await fetch ( script . src ) . then ( ( res ) => res . text ( ) ) ;
@@ -59,37 +56,20 @@ function sketchVerifier(p5, fn) {
59
56
} else {
60
57
return script . textContent ;
61
58
}
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
+ } ,
81
60
82
61
/**
83
62
* Extracts the user-defined variables and functions from the user code with
84
63
* the help of Espree parser.
85
- *
64
+ *
65
+ * @private
86
66
* @method extractUserDefinedVariablesAndFuncs
87
67
* @param {string } code - The code to extract variables and functions from.
88
68
* @returns {Object } An object containing the user's defined variables and functions.
89
69
* @returns {Array<{name: string, line: number}> } [userDefinitions.variables] Array of user-defined variable names and their line numbers.
90
70
* @returns {Array<{name: string, line: number}> } [userDefinitions.functions] Array of user-defined function names and their line numbers.
91
71
*/
92
- fn . extractUserDefinedVariablesAndFuncs = function ( code ) {
72
+ extractUserDefinedVariablesAndFuncs : function ( code ) {
93
73
const userDefinitions = {
94
74
variables : [ ] ,
95
75
functions : [ ]
@@ -142,26 +122,27 @@ function sketchVerifier(p5, fn) {
142
122
}
143
123
144
124
return userDefinitions ;
145
- }
125
+ } ,
146
126
147
127
/**
148
128
* Checks user-defined variables and functions for conflicts with p5.js
149
129
* constants and global functions.
150
- *
130
+ *
151
131
* This function performs two main checks:
152
132
* 1. Verifies if any user definition conflicts with p5.js constants.
153
133
* 2. Checks if any user definition conflicts with global functions from
154
134
* p5.js renderer classes.
155
- *
135
+ *
156
136
* If a conflict is found, it reports a friendly error message and halts
157
137
* further checking.
158
- *
138
+ *
139
+ * @private
159
140
* @param {Object } userDefinitions - An object containing user-defined variables and functions.
160
141
* @param {Array<{name: string, line: number}> } userDefinitions.variables - Array of user-defined variable names and their line numbers.
161
142
* @param {Array<{name: string, line: number}> } userDefinitions.functions - Array of user-defined function names and their line numbers.
162
143
* @returns {boolean } - Returns true if a conflict is found, false otherwise.
163
144
*/
164
- fn . checkForConstsAndFuncs = function ( userDefinitions , p5Constructors ) {
145
+ checkForConstsAndFuncs : function ( userDefinitions , p5 ) {
165
146
const allDefinitions = [
166
147
...userDefinitions . variables ,
167
148
...userDefinitions . functions
@@ -172,78 +153,85 @@ function sketchVerifier(p5, fn) {
172
153
// redefinition, the line number in user's code, and a link to its
173
154
// reference on the p5.js website.
174
155
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 } ` ;
177
158
return message ;
178
159
}
179
160
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
-
197
161
// Checks for constant redefinitions.
198
162
for ( let { name, line } of allDefinitions ) {
199
163
const libDefinition = constants [ name ] ;
200
164
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 ;
204
168
}
205
169
}
206
170
207
171
// The new rules for attaching anything to global are (if true for both of
208
172
// the following):
209
- // - It is a member of p5.prototype
173
+ // - It is a member of p5.prototype
210
174
// - Its name does not start with `_`
211
175
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' )
213
178
) ;
214
179
215
180
for ( let { name, line } of allDefinitions ) {
216
181
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 ;
221
185
}
222
186
}
223
187
224
188
return false ;
225
- }
189
+ } ,
226
190
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 ) ;
235
206
236
- const userCode = await fn . getUserCode ( ) ;
237
- const userDefinedVariablesAndFuncs = fn . extractUserDefinedVariablesAndFuncs ( userCode ) ;
207
+ return userCode ;
208
+ } ,
238
209
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 ) ;
242
218
}
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
+ } ;
243
231
}
244
232
245
233
export default sketchVerifier ;
246
234
247
235
if ( typeof p5 !== 'undefined' ) {
248
236
sketchVerifier ( p5 , p5 . prototype ) ;
249
- }
237
+ }
0 commit comments