This seems like a problem that requires some JS expertize that I'm apparently not in posses. I'm writing a scripting module for an app. The scripting and the app are in Javascript.
It will be used by developers to provide scripting extensions to various modules that run on demand or when triggered by something. I've got a request to alter the way it works in order to simplify the coding of the scripts. And I'm kinda stuck since I don't know how to proxy local variables inside the script into an external object.
This is a sample of what works currently:
// this is part of the app developer API
function compileScript(userScript, argSignature) {
let _scriptFunc_ = null;
/* scripting API - there are available in scripts */
const print = function(message, window) {
const msg = document.createElement("span")
msg.innerText = message;
document.getElementById("output").append(msg)
document.getElementById("output").append(document.createElement("br"))
};
/* end Scripting API section */
try {
const scriptSource = `
(async () => {
try {
${userScript}
} catch (err) {
//_errEmit.fire(err)
}
})()
`; // wrap the execution in async so they can use await in their userScript
// argument signatures are defined by the module that "compiles" the script
// they call it with those and use them inside the userScript
eval("_scriptFunc_ = function(" + argSignature + ") {\n" + scriptSource + "\n}");
}
catch (err) {
//EvtScriptEmitEvalError.fire(err); // script "compilation" exception
return null;
}
return _scriptFunc_.bind(this);
}
// USAGE
// have some context with "variables" inside accessible by the script
// changes to this might be propagated elsewhere if it has property getters/setter
let scriptData = {
something: 10
};
// define the script
let userScript = `
for (let i = 0; i < count; i++) {
this.something++; // this changes scriptData
print(this.something)
}
`
// "compile" and run
const script = compileScript.call(scriptData, userScript, "count")
script(5) // output: 11,12,13,14,15
console.log(scriptData.something) // 15
<pre id="output">
</pre>
The request they have is to access the properties of the scriptData object inside the script by simply referring to its properties as if they were variables inside the script.
This is how they would want to write it (note there is no "this." before something):
let userScript = `
for (let i = 0; i < count; i++) {
something++; // this changes scriptData
print(something)
}
`
They are fine with possible name collisions between parameters and members of scriptData, it is developer work to set that up correctly.
My problem, tough, is that I don't have any idea how to modify "compileScript" in order to inject members of scriptData as plain variables inside the script is such a way that they proxy to the scriptData object. It is easy to define a function in the scope of compileScript like "print", but I have no ideas on how to do this concept of "proxy variables".
"with" is not available in strict mode which the app runs in. JS Proxy class does not seem useful. Deconstructing scriptData into variables can be done but those variables are no longer going to the scriptData object, they are local. Defining property getters/setters is available only for objects, not for the compileScript function...
I cannot modify the scriptData object, the user passes it as is. I can only tweak the generation of the script so that it behaves as required.
It should also work in a web worker (so no global scope / window), since a script could be triggered at the completion of a web worker.
Any ideas?