Can I Import JavaScript Functions from Other Files into JXA?

I have a file that contains a number of functions (not dealing with HTML/CSS). Is there a way to import them into JXA so that I can use them in JXA?

from what I understood, do you want to Execute Script from a File?

https://wiki.keyboardmaestro.com/JavaScript_for_Automation?s[]=file

"The JXA script may be accessed as text in the Action, or from a file"

Thanks. But that does not answer my question. My question is how to import the functions in another file into the current working JXA script.

1 Like

If you’re talking about using the file as a library you can just import it like any script library.

See, for example, https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/Importing-Scripts

1 Like

It may in practice be simpler to copy-paste your functions across, but it is possible to use the Function / eval pattern (which has, to be honest, a deservedly sulphurous reputation).

I tend to do this rather than using the JXA Library mechanism (referenced in the previous post) which I find occasionally a bit fragile, and generally not able to cope with any cross-dependencies between the functions in the library (each has to be stand-alone).

Function/eval:

// Evaluate a function f :: (() -> a)
// in the context of the JS libraries whose source
// filePaths are listed in fps :: [FilePath]
// usingLibs :: [FilePath] -> (() -> a) -> a
const usingLibs = fps =>
    f => {
        const gaps = fps.filter(fp => !doesFileExist(fp));

        return 1 > gaps.length ? Function(
            `"use strict";
                ${fps.map(readFile).join("\n\n")}
                return (${f})();`
        )() : `Library not found at: ${gaps.join("\n")}`;
    };

For a fuller example:

Expand disclosure triangle to view JS Source
/* eslint-disable no-new-func */
(() => {
    "use strict";

    // main :: IO ()
    const main = () => {

        const inner = () =>
            sj(
                foldl(add)(0)(
                    ft(1)(10)
                )
            );

        return inner();
    };

    // ----------------- LIBRARY IMPORT ------------------

    // Evaluate a function f :: (() -> a)
    // in the context of the JS libraries whose source
    // filePaths are listed in fps :: [FilePath]

    // Evaluate a function f :: (() -> a)
    // in the context of the JS libraries whose source
    // filePaths are listed in fps :: [FilePath]
    // usingLibs :: [FilePath] -> (() -> a) -> a
    const usingLibs = fps =>
        f => {
            const gaps = fps.filter(fp => !doesFileExist(fp));

            return 1 > gaps.length ? Function(
                `"use strict";
                ${fps.map(readFile).join("\n\n")}
                return (${f})();`
            )() : `Library not found at: ${gaps.join("\n")}`;
        };

    // doesFileExist :: FilePath -> IO Bool
    const doesFileExist = strPath => {
        const ref = Ref();

        return $.NSFileManager.defaultManager
            .fileExistsAtPathIsDirectory(
                $(strPath)
                .stringByStandardizingPath, ref
            ) && 1 !== ref[0];
    };

    // readFile :: FilePath -> IO String
    const readFile = fp => {
        const
            e = $(),
            ns = $.NSString.stringWithContentsOfFileEncodingError(
                $(fp).stringByStandardizingPath,
                $.NSUTF8StringEncoding,
                e
            );


        return ObjC.unwrap(
            ns.isNil() ? (
                e.localizedDescription
            ) : ns
        );
    };

    // ---------------------- MAIN -----------------------
    // https://github.com/RobTrew/prelude-jxa
    return usingLibs([
        "~/prelude-jxa/jsPrelude.js",
        "~/prelude-jxa/jxaSystemIO.js",
        "~/jsParserCombinators/parserCombinators.js"
    ])(main);
})();
1 Like

Thanks, @hello and @tiffle.
I don't quite understand them yet. But I'll study them.