Skip to content

Latest commit

 

History

History
88 lines (70 loc) · 2.96 KB

20191120.md

File metadata and controls

88 lines (70 loc) · 2.96 KB

eval()

So like you shouldn't use eval() or the Function constructor cause they can be security vulnerabilities for script injection. But what if you have to?

I previously used the Function constructor for my GQLinJSX project. To take server-side parsed JSX and render the output, chef's kiss.

The case

So webworkers cannot use ESM imports which sucks, but we have a couple of functions we want to use within the runpkg project, in this case a url parsing function. It looks something like the following:

const parseUrl = (url = window.location.search.slice(1).replace(/\/$/, "")) => {
    const parts = url
        .trim()
        .replace("https://unpkg.com", "")
        .split("/")
        .map(part => part.trim())
        .filter(Boolean);

    if (parts[0]) {
        // checks if scoped packaged
        if (parts[0].startsWith("@")) {
            const nameVersion = parts[1].split("@");
            return {
                name: `${parts[0]}/${nameVersion[0]}` || null,
                version: nameVersion[1] || null,
                path: parts.join("/") || null,
                file:
                    (parts.length > 2 && parts.slice(parts.length - 1)[0]) ||
                    null,
                directory: parts.slice(2, parts.length - 1).join("/") || null
            };
        } else {
            const nameVersion = parts[0].split("@");
            return {
                name: nameVersion[0] || null,
                version: nameVersion[1] || null,
                path: parts.join("/") || null,
                file:
                    (parts.length > 1 && parts.slice(parts.length - 1)[0]) ||
                    null,
                directory: parts.slice(1, parts.length - 1).join("/") || null
            };
        }
    } else {
        return {
            name: null,
            version: null,
            path: null,
            file: null,
            directory: null
        };
    }
};

export { parseUrl };

So how do we go from this to an importable script?

❌ Use a bundler but NO BUILD!.

❌ Use an ESM polyfill (kinda boring though and I don't want to research the smallest one).

🤦‍♀️ Use eval and strip off export and function declarations.

The final code looks like this:

const getParseUrl = () =>
    fetch("/utils/parseUrl.js")
        .then(x => x.text())
        .then(x =>
            x.replace(/\s*export {[^}]+\};/g, "").replace(/^const\s[^=]+=/, "")
        );

Then we call it as such:

const { name, version } = eval(await getParseUrl())(url);

It's hacky but it sorta works.

The benefits

So now maintenance is easier because if we update parseUrl function it will get updated in the dependencyFetch webworker. ✅ Provided we don't refactor the structure of parseUrl but that's a problem for future Kara. 👍