Importing Scripts/Sharing code (JXA)

Has anyone had luck with sharing code between scripts? I have some functions I would like to create once and share across scripts so that they don’t get out of sync with copy/pastes. I have read all the Applescript docs on how to create Library functions, and I can get them to work in the global context, but they never return when called inside of a TP script/Anonymous function.

// works in global context
var app = Application(“TaskPaper-compiled”); //only works when compiled !?!
const calculatedPriority = tp.calculatePriority(item);

// does not work in TPContext:
(function () {
function TaskPaperContext(editor, options) {
var app = Application(“TaskPaper-compiled”); //only works when compiled !?!
const calculatedPriority = tp.calculatePriority(item);

Any ideas?

The key things to bear in mind are that

  1. the TPContext function is passed as a string (from the JXA interpreter to TaskPaper’s JSContext instance)
  2. The TPContext is effectively isolated from the file system.

That means that if you want to pull any library code into the TPContext function, you need to do it:

  • in the JXA context (which has file-system access) before the TPContext code is passed by one interpreter to the other,
  • by means of string inclusion.

In a different context, FWIW, I regularly pull library code in by string inclusion using this pattern:

Template
(() => {
    'use strict';

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

        const inner = () => {
            return sj(
                foldl(a => b => a + b)(
                    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 ? eval(
            `(() => {
                '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
            ) && ref[0] !== 1;
    };

    // 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 ------------------------------------------------
    return usingLibs([
        '~/prelude-jxa/jsPrelude.js',
        '~/prelude-jxa/jxaSystemIO.js'
        // '~/Library/Script Libraries/BBDrafts.js'
    ])(main);
})();

Wow, that is some next-level stuff. I am going to have to stare at that a little bit. Thank you very much for providing that info.

Very cool…functional programming at its finest…took me a bit to figure it out, but I think I got it now. However, when trying to execute the code by itself with one file and no code added, here’s what I get:

execution error: Error on line 37: TypeError: undefined is not an object (evaluating ‘global.TYPED_ARRAY_SUPPORT’) (-2700)

The kind folks on the Web forums of the world point to that error being a babel issue and say to remove “env” from “presets”. Not sure how that applies here in the world of JXA. Any ideas?

I think you would have to show us your source code, with the imported file and its path.

You are using Babel ?

Thank you. I am not using babel – that’s just what people on the Internet says causes this error. I am just running the importing template code you pasted above as JXA from VSCode (osascript…) without any changes other than specifying an empty (or basic JS) file where the file path strings are. That should work, right? Does that template run for you as is?

It runs fine here.

As I say, we would need to look at the file you are pulling in, and the code which tries to use functions in it.

(It may be helpful to look at the examples of jsPrelude.js etc here:

https://github.com/RobTrew/prelude-jxa

)

PS It seems unlikely that you are using an ES5 JS (or pre macOS Sierra) interpreter, but the code would certainly need adjustment for that.