nodeautomation

A high-level Apple event (“AppleScript”) bridge for macOS.

Usage no npm install needed!

<script type="module">
  import nodeautomation from 'https://cdn.skypack.dev/nodeautomation';
</script>

README

README

NodeAutomation is a Node.js module that allows 'AppleScriptable' applications to be controlled directly from JavaScript.

For example, to get the value of the first paragraph of the document named 'README' in TextEdit:

app('TextEdit').documents.named('README').paragraphs[0].get()

This is equivalent to the AppleScript statement:

tell application "TextEdit" to get paragraph 1 of document "README"

Or to create a new "Hello World!" document in TextEdit:

app('TextEdit').make({new: k.document, 
                      withProperties: {text: "Hello World!"}})

Caution: This is an alpha release. There will be bugs and rough edges.

Documentation is preliminary, being a quick and dirty translation of the original appscript manual.

E&OE. No warranty given. Use at own risk. Etc.

(See also: http://appscript.sourceforge.net/status.html)


Install (from https://www.npmjs.com/package/nodeautomation):

npm install nodeautomation

See: node_modules/nodeautomation/doc/index.html for documentation.


Dependencies:

  • nodobjc (note: this requires XCode)

Test:

$ node
> require('nodeautomation/global')
> const fn = app('Finder')
> fn.home()
app('Finder').startupDisk.folders.named('Users').folders.named('jsmith')

Known issues:

Unlike AppleScript, which retrieves application terminology in compact, proven binary AETE format, NodeAutomation currently uses the newer (and more bloated) XML-based SDEF support. Historically this has been problematic when obtaining terminology from older Carbon-based apps that still use AETEs due to bugs in macOS’s built-in AETE-to-SDEF converter, resulting in missing/corrupted terms. Hopefully these bugs are eliminated in newer versions of macOS, but further testing will be required to determine if it's safe to rely on SDEF only, or if AETE support will be needed to ensure full AppleScript parity.

In addition, very large application dictionaries (e.g. Adobe, Microsoft apps) can be slow to parse due to the size of the XML and the overhead of crossing the JS-ObjC bridge when making very large numbers of NSXML API calls. (Parsing SDEFs requires XInclude support, so lightweight native JS parsers probably won't cut it, and full XML parsers like lxml require C compilers to install.) If startup times prove to be a problem, the workaround will be to export the application's terminology to a static glue module, then import that at runtime, avoiding the need to parse its SDEF every time.

Ongoing bitrot in NodObjC/BridgeSupport/Apple event APIs is liable to break NodeAutomation and other third-party software that rely on such technologies. Caveat emptor: macOS is developer quicksand for anything other than Swift+iOS.