Is there a way to run JavaScript from a string in browser with imported libraries?

39 Views Asked by At

I have a function that lets me run a JavaScript function in the browser:

export async function executeFunction(data: LoadOutput, input: Record<string, any>): Promise<any> {
    let args = [];
    for (let param of data.functionTree[0].parameters) {
        let value = input[param.name];

        if(!value) {
            return undefined;
        }

        args.push(value);
    }

    let func = new Function('return ' + data.jsCode)();
    return await func.call(null, ...args);
}

It all works, but now I want to add more functionality by being able to use external libraries. I tried adding imports in the string but this didn't work and resulted in errors

There was an error: Module name, 'image-conversion' does not resolve to a valid URL.

Is there a way I can import external libraries? Preferably by not having to pass them as a parameter (That's what ChatGPT suggested but would require a massive refactor of my code)

Is there maybe a library that does the same thing as Function but lets me add external libraries to the scope?

For additional information: I'm using SvelteKit

2

There are 2 best solutions below

0
Finn On BEST ANSWER

I now managed to do it. I imported a library (I used lodash as an example for testing) and saved it into a const

import lodash from 'lodash';
const libraries = { lodash }

I pass it into the Function call like this

let func = new Function(...Object.keys(libraries), 'return ' + data.jsCode)(...Object.values(libraries));
return await func.call(null, ...args);

And now I can use lodash when calling a function from a string.

Here is my testing example:

"libraryTest": "function libraryTest(text: string): { result: string } {\n" +
    "    const result = lodash.toUpper(text);\n" +
    "    return { result };\n" +
    "}"
0
brunnerh On

Such dependencies cannot be fully dynamic unless you somehow ship all your node modules and do some complicated on-the-fly bundling, but you can probably e.g. somewhere import the module via an URL import and use that.

import imageConversionUrl from 'image-conversion?url';
`import ... from ${JSON.stringify(imageConversionUrl)}`

URL imports bundle the dependency and output it as an asset in the build.
(More commonly used for media assets so they can be used e.g. as an image src. For a list of file types URL imports are the implicit default.)