javascriptnode.jsterminalapplescriptjavascript-automation

Mac Automation - AppleScript, JavaScript for Automation (.JXA), and JavaScript (.JS)


I learned AppleScript last year and it's gone pretty well. After discovering BetterTouchTool, I learned about utilizing JXA or JS for automating tasks.

I can make sense of the .jxa documentation on apple's Apple's JXA Release Notes

I can also write very simple scripts with BTT's JavaScript functions(?)

I have a few questions which I've googled answers to, but I don't think they apply to me.

  1. I was told by someone on another forum that I should switch to using JXA instead of AppleScript because it's more current. I researched it and it looks like both AS and JXA aren't being developed further. Is there a reason why I should re-write my libraries to JXA? (next question might provide insight what I want)

2. Can I write apps/libraries that use any combination of these three languages? AppleScript is VERY limited in the sense of working with dictionaries (records) and string manipulation etc... I know JavaScript has so many functions built into it- which would make my code so much cleaner (and more ummm... reliable? some of my "from scratch" functions are absolute trash). It doesn't look like JXA adapts any of these functions, right? I've found a few JXA/AS libraries people post on GitHub that essentially replicate these. Am I going about this the right way? I'll put my project's requirements at the end

I started diving into node.js (which is what I think the BTT's JavaScript Functions link uses. I know THAT can call AppleScript functions as also mentioned in that link. (I posted another topic on this here if anyone has a solution to that too. Also, I know AppleScript can do the terminal command to run a javascript file- my issue is passing inputs/return values to each other... and also creating an organized structure.

Project: (100% for personal use and not to distribute- mostly because it basically requires every permission enabled) -communicate with Apple Music to gather metadata, easily work with playlists- add, delete, etc.. -backup my metadata (currently using Numbers, but might switch to SQL?) -icon automation which requires file and image manipulation (Pixelmator Pro has an extensive AS dictionary, so I'm using that) -then using these scripts in conjunction with BTT to create cool UI's (automating the buttons etc uses JSON to transfer data between the code and BTT- I wrote a very sloppy AS function to "encode/decode" JSON. The buttons and icons will constantly be changing, so automating these needs to be easy- not spaghetti code like I have (with my personal libraries and icky JSON en/decoder)

Oh and last thing... I stumbled upon Obj C with AS or JXA, but I am already overwhelmed with the 3 languages mentioned. Is it worth diving into this a bit more? Oh and I'm realizing using terminal commands/scripts might also be worth diving into a bit more?

I hope this question is concise because it definitely is not in my head. I apologize if I provided excessive info.

Thanks in advance!


Solution

  • Is there a reason why I should re-write my libraries to JXA?

    There is no external reason why you should rewrite your code. As others have written in the comments, JXA omits some application communication features that AppleScript supports. There is no reasonable sense in which JXA is “more current” than AppleScript. There are reasonable senses in which AppleScript is more complete than JXA, mainly in handling communication between applications. This is why AppleScript was created.

    AppleScript… allows users to directly control scriptable Macintosh applications, as well as parts of macOS itself… to automate repetitive tasks, combine features from multiple scriptable applications, and create complex workflows.

    I can find no equivalent document with a statement of purpose for JXA. The closest is the Introduction to JavaScript for Automation Release Notes, which says that “JavaScript for Automation provides the ability to use JavaScript for interapplication communication between apps in OS X.”. I would also point out that while the latter document specifically states that “This document is no longer being updated”, both of these two documents are in the archive section of Apple’s developer site; that section is specifically for libraries “no longer being updated.”

    I see only a few reasons to prefer AppleScript over JXA either. JXA’s incompleteness is the most obvious: if JXA doesn’t support a feature that AppleScript has, then you’ll need to use AppleScript or work around that lack. But as you noted, JavaScript is much better at data manipulation, especially strings, than AppleScript is. If you need regular expressions, for example, and JXA supports the features you need for app communication, then you’ll find it much easier to write your script in JavaScript than in AppleScript.

    Conversely, if your script does little data manipulation and is mostly about communication between applications on your Mac, you’ll probably find it easier to use AppleScript.

    Can I write apps/libraries that use any combination of these three languages?

    As you noted, both Objective C and node.js can call AppleScript or JXA scripts. In fact, most scripting languages on the Mac can call each other. Swift scripts can easily call AppleScripts, AppleScripts (as you noted) can easily shell out to the command line. Ditto for Perl and Python, and even Automator.

    Is it worth diving into more languages? In your case, it sounds like you want to be able to combine the best features of multiple environments. So it likely is worth diving into more languages and more environments, so as to be able to extract the best features of each.

    Much of this is going to be opinion, of course, even including whether some code is cleaner than others. To pull from a script that I wrote for taking a screenshot, which of these is cleaner is likely to depend on what you’re familiar with:

    AppleScript:

    --take the screenshot
    tell application "System Events"
        keystroke "$" using {command down}
        delay 0.1
        keystroke " "
        keystroke return
    end tell
    

    JavaScript/JXA:

    system = Application("System Events");
    //take the screenshot
    system.keystroke("$", {using: ['command down']});
    delay(0.1);
    system.keystroke(" ");
    system.keystroke("\r");
    

    Ultimately, the question is likely to be answered by asking whether you plan to do a lot of data manipulation or whether you plan to do a lot of communication with other applications. AppleScript excels at the latter, JavaScript at the former. I would not, for example, want to write the following in AppleScript:

    //create filename using the current card number from the window title
    titleRegex = new RegExp("^(.+[^ ])  *([1-9][0-9]*) *\/ *([1-9][0-9]*)$");
    titleParts = titleRegex.exec(title);
    filename = titleParts[1];
    currentCard = titleParts[2];
    totalCards = titleParts[3];
    

    You can see this difference throughout the archived Mac Automation Scripting Guide, for example, Manipulating Text.

    Sadly, however, I did end up rewriting a far less robust AppleScript version:

    --get the current card number and the total card count from the title
    set titleReversed to reverse of characters of stackTitle as string
    set slashLocation to (length of stackTitle) - (offset of " / " in titleReversed)
    set spacesLocation to (length of stackTitle) - (offset of "  " in titleReversed)
    set totalCards to characters (slashLocation + 1) thru -1 of stackTitle as string as number
    set currentCard to characters (spacesLocation) thru (slashLocation - 1) of stackTitle as string as number
    

    Why? Because elsewhere in the script it was far more readable (for me) to manipulate this particular app using AppleScript than using JavaScript.

    Finally, inter application communication is handled with Apple Events, which doesn’t require AppleScript or JXA. You can, if you wish, roll your own in Objective C or Swift, such as in this example of getting paragraphs from TextEdit.