Skip to content

Commit

Permalink
Merge pull request #7293 from sproutleaf/dev-2.0
Browse files Browse the repository at this point in the history
Add new sketch verifier to FES
  • Loading branch information
davepagurek authored Oct 13, 2024
2 parents 5eaedaa + 07f4b1b commit ef1b09c
Show file tree
Hide file tree
Showing 7 changed files with 441 additions and 32 deletions.
75 changes: 65 additions & 10 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
},
"version": "1.9.4",
"dependencies": {
"acorn": "^8.12.1",
"acorn-walk": "^8.3.4",
"colorjs.io": "^0.5.2",
"file-saver": "^1.3.8",
"gifenc": "^1.0.3",
Expand Down
47 changes: 25 additions & 22 deletions preview/index.html
Original file line number Diff line number Diff line change
@@ -1,41 +1,44 @@
<!DOCTYPE html>
<html>

<head>
<title>P5 test</title>
<meta http-equiv="pragma" content="no-cache" />
<meta http-equiv="cache-control" content="no-cache" />
<meta charset="utf-8">

<style>
body{
margin:0;
overflow: hidden;
}
body {
margin: 0;
overflow: hidden;
}
</style>
</head>

<body>
<script type="module">
import p5 from '../src/app.js';
// import calculation from './src/math/calculation.js';
<script type="module">
import p5 from '../src/app.js';
// import calculation from './src/math/calculation.js';

// p5.registerAddon(calculation);
// p5.registerAddon(calculation);

const sketch = function(p){
p.setup = function(){
p.createCanvas(200, 200);
};
const sketch = function (p) {
p.setup = function () {
p.createCanvas(200, 200);
};

p.draw = function(){
p.background(0, 50, 50);
p.circle(100, 100, 50);
p.draw = function () {
p.background(0, 50, 50);
p.circle(100, 100, 50);

p.fill('white');
p.textSize(30);
p.text('hello', 10, 30);
};
};
p.fill('white');
p.textSize(30);
p.text('hello', 10, 30);
};
};

new p5(sketch);
</script>
new p5(sketch);
</script>
</body>

</html>
2 changes: 2 additions & 0 deletions src/core/friendly_errors/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import validateParams from './param_validator.js';
import sketchVerifier from './sketch_verifier.js';

export default function (p5) {
p5.registerAddon(validateParams);
p5.registerAddon(sketchVerifier);
}
126 changes: 126 additions & 0 deletions src/core/friendly_errors/sketch_verifier.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import * as acorn from 'acorn';
import * as walk from 'acorn-walk';

/**
* @for p5
* @requires core
*/
function sketchVerifier(p5, fn) {
/**
* Fetches the contents of a script element in the user's sketch.
*
* @method fetchScript
* @param {HTMLScriptElement} script
* @returns {Promise<string>}
*/
fn.fetchScript = async function (script) {
if (script.src) {
try {
const contents = await fetch(script.src).then((res) => res.text());
return contents;
} catch (error) {
// TODO: Handle CORS error here.
console.error('Error fetching script:', error);
return '';
}
} else {
return script.textContent;
}
}

/**
* Extracts the user's code from the script fetched. Note that this method
* assumes that the user's code is always the last script element in the
* sketch.
*
* @method getUserCode
* @returns {Promise<string>} The user's code as a string.
*/
fn.getUserCode = async function () {
// TODO: think of a more robust way to get the user's code. Refer to
// https://github.com/processing/p5.js/pull/7293.
const scripts = document.querySelectorAll('script');
const userCodeScript = scripts[scripts.length - 1];
const userCode = await fn.fetchScript(userCodeScript);

return userCode;
}

/**
* Extracts the user-defined variables and functions from the user code with
* the help of Espree parser.
*
* @method extractUserDefinedVariablesAndFuncs
* @param {string} code - The code to extract variables and functions from.
* @returns {Object} An object containing the user's defined variables and functions.
* @returns {Array<{name: string, line: number}>} [userDefinitions.variables] Array of user-defined variable names and their line numbers.
* @returns {Array<{name: string, line: number}>} [userDefinitions.functions] Array of user-defined function names and their line numbers.
*/
fn.extractUserDefinedVariablesAndFuncs = function (code) {
const userDefinitions = {
variables: [],
functions: []
};
// The line numbers from the parser are consistently off by one, add
// `lineOffset` here to correct them.
const lineOffset = -1;

try {
const ast = acorn.parse(code, {
ecmaVersion: 2021,
sourceType: 'module',
locations: true // This helps us get the line number.
});

walk.simple(ast, {
VariableDeclarator(node) {
if (node.id.type === 'Identifier') {
const category = node.init && ['ArrowFunctionExpression', 'FunctionExpression'].includes(node.init.type)
? 'functions'
: 'variables';
userDefinitions[category].push({
name: node.id.name,
line: node.loc.start.line + lineOffset
});
}
},
FunctionDeclaration(node) {
if (node.id && node.id.type === 'Identifier') {
userDefinitions.functions.push({
name: node.id.name,
line: node.loc.start.line + lineOffset
});
}
},
// We consider class declarations to be a special form of variable
// declaration.
ClassDeclaration(node) {
if (node.id && node.id.type === 'Identifier') {
userDefinitions.variables.push({
name: node.id.name,
line: node.loc.start.line + lineOffset
});
}
}
});
} catch (error) {
// TODO: Replace this with a friendly error message.
console.error('Error parsing code:', error);
}

return userDefinitions;
}

fn.run = async function () {
const userCode = await fn.getUserCode();
const userDefinedVariablesAndFuncs = fn.extractUserDefinedVariablesAndFuncs(userCode);

return userDefinedVariablesAndFuncs;
}
}

export default sketchVerifier;

if (typeof p5 !== 'undefined') {
sketchVerifier(p5, p5.prototype);
}
Loading

0 comments on commit ef1b09c

Please sign in to comment.