How to get going with javascript and Omnifocus?

To be fair, if you actually put the script in a file, instead of using the “here” document presented, you don’t have to worry about escaping the shell as much. Removes a layer of interpolation.

That’s interesting – do you want to show us a working example ?

(I may be misunderstanding what you mean by if you actually put the script in a file – the example above is a .sh file)

The heredoc in that file provides a way of submitting more than one line of code to the osascript command, given than the -e switch takes only a single line of inline code.

But perhaps you are talking about a shell script which contains no JavaScript – pointing the -e switch instead to the path of an external .scpt or .applescript file ?


That can certainly work, but then I am no longer sure that I understand the context of your “Yes, I really want to do it in the shell.”

If the JXA code is in a .scpt file, then it can be run directly, without involving a shell script, so I’m not sure exactly what your reference to (or need for) the shell really involves in concrete terms ?

This is how I would do the same thing, but avoid the shell problem you’re having:
Create a file called script.js and put the script into it but no escapes. It’s pure javascript. You just need the right #! line at the top.
So here is your script as a pure javascript on MacOS. You should be able to run this straight up as script.js just like any other script assuming you make it executable. Notice how the first line invoked osascript and turns the file into a javascript. No quoting needed now.

#!/usr/bin/osascript -l JavaScript
(() => {
 //   use strict;

    // omniJS :: () -> IO String
    const omniJS = () => [
        `Inbox length: ${inbox.length}`,
        `Flattened projects length: ${flattenedProjects.length}`
    ].join("n");

    // main :: IO ()
    const main = () =>
        evalOmniJSWithArgs("OmniFocus", omniJS);

    // --------------------- GENERIC ---------------------

    const evalOmniJSWithArgs = (appName, f, ...args) =>
        Application(appName).evaluateJavascript(
            `(${f})(${args.map(JSON.stringify)})`
        );

    return main();
})();

And here is the output:

rcook@MacBook-Pro-2021 (wealthyvault (master)): ./bin/test.js 
Inbox length: 8nFlattened projects length: 29
2 Likes

Not, as it happens, managing to run that here, but I get a sense of what you are doing :-)


If the script is going to be in a file of its own, I would personally:

  • drop the #! line at the top
  • save in a file with the .scpt for writing and testing in Visual Studio Code with the Run Script plugin (or in Script Editor)
  • and run from the shell, when need arises, by passing a path to osascript

e.g.

osascript -l JavaScript "./sample.scpt"
1 Like

This last version of the technique works perfectly. It makes it very easy and could come in handy. Thanks!

1 Like

Not sure why the script doesn’t work on your machine. It’s not doing anything special.

Calling osascript -l Javascript scriptname.scp is exactly the same as running a script that starts with #!osacript -l Javascript The only reason it would not work is if osascript is not in your path or your script is not executable. You can fix this last with chmod 755 script.js

Anyhow, good to have multiple solutions right?

1 Like

Probably not the issue – I’ve made it executable with chmod 755 script.js, but there may be some other subtlety or difference between our systems.
It runs, but errors on unexpected syntax when it sees line 2.

Is it complaining about use strict? That’s why this is commented out in my copy. It works fine without that. This is where my javascript weakness will show up. :-)
By the way, the fact that it complains about line 2 means it’s executing in a java environment… :-) Line 1 replaces the shell I think using “exec” with python
Oh, one more issue on MacOS: if you run xattr -l script.js | grep quarantine and it contains “com.apple.quarantine” then it means the script has been quarantined because it’s unauthorized 3rd pary application. To fix, you must run xattr -d com.apple.quarantine script.js.
This last does not match your problem but it’s a likely one for people using this method because of Apple’s SIP system

That should read "use strict"; with plain double quotes – fancy quotes sometimes arise from cut and paste histories.

Another issue in your JS source there (perhaps also a cut and paste scar ?) is that you have:

join("n");

where the orginal had (and needs)

join("\n");
1 Like

Ah, thanks. Well, if you use quotes, did it run for you? Because it works for me if I just comment it out.
Sorry about the \n typo, I noticed that but didn’t bother with it, my bad. When I fix that typo and put in quotes, I get the expected result.

rcook@MacBook-Pro-2021 (wealthyvault (master)): ./bin/example.js 
Inbox length: 8
Flattened projects length: 30

it works for me if I just comment it out.

I wonder if you are trading one set of character-escaping issues for another ?

osascript -l JavaScript "./someFile.scpt"`

does side-step that kind of thing.

Good enough for me – I personally reserve the .js extension for other JS contexts – and in practice I use the specialised actions of the deep and excellent Keyboard Maestro (Execute JavaScript for Automation etc) for osascript JS.

Interesting, I didn’t think I was using any escapes in my file. What character escape issues do you see with my suggested method? Is it not normal to put quotes around “use strict”? I don’t know enough javascript to see any escapes going on. Is it the backticks?
As an experiment, I took the full text of my file without the #! and it compiled fine in Script Editor, so I think it’s valid javascript with no special shell escaping going on.

I just mean the need to comment out the "use strict", before it will work on your system.

Not sure what’s going on there.

(Incidentally, strict mode just yields fuller and more helpful messages when things go wrong)

1 Like

Looks like I edited my comment while you were typing, but it appears that the need for quotes is an Apple thing, not just because I’m using a shell script in the way I am, as it is valid in my applescript too.

Thanks for that!