-
Notifications
You must be signed in to change notification settings - Fork 119
Importing Scripts
This page lists various approaches of importing JavaScript code from other files.
JXA supports loading of 'script libraries' out of the box.
By declaring global functions and saving the file as <name>.scpt in an appropriate location,
you can require it by calling Library("<name>") in the script that uses the library functions.
The library script can be in one of the following locations:
~/Library/Script Libraries/-
Contents/Library/Script Libraries/in an application bundle. (support added in OSX 10.11 'El Capitan') - A directory listed in the colon-separated list for the environment variable
OSA_LIBRARY_PATH. (support added in OSX 10.11 'El Capitan')
Imagine that we can use JavaScript libraries likes Ramda in JavaScript for Automation using a simple require call! It's now possible, thanks to browserify.
Using Browserify, you can bundle a Node.js script with all their dependencies to run on a JXA environment. However, some things need to be taken care of:
- No
globalandwindowvariable exist, but the global object can be accessed usingthisat the top level. (At lower scopes you should be able to find it under the name ofglobalThis) Some node modules checks forglobalandwindowinstead ofthis. - The resulting "browserified" script returns a function, which will always be printed when execution finishes.
With that in mind, we can create a command to run a Node.js script like this:
(echo 'global = window = this;'; browserify file.js; echo ';ObjC.import("stdlib");$.exit(0)') | osascript -l JavaScriptOne benefit of this approach is that the resulting script is portable, because all dependencies are bundled up inside a single JavaScript file.
This CoffeeScript code shows the name of the largest file that is selected in Finder. It depends on _.max from Lo-Dash.
largest.coffee:
{ max } = require 'lodash'
largest = max Application('Finder').selection(), (f) -> f.size()
console.log "Largest file is #{largest.name()}"Make sure you installed the required libraries and tools:
npm install -g browserify
npm install coffeeify lodashNow we can use coffeeify to allow browserify to compile CoffeeScript, and pipe the result directly to osascript.
(echo 'window = this;'; browserify -t coffeeify largest.coffee; echo ';ObjC.import("stdlib");$.exit(0)') | osascript -l JavaScriptnpm is a place to host any JavaScript package, not just Node packages. Imagine that JXA-specific modules are available on npm... You'd be able to require a library to use the file system without having to deal with all the Objective-C complexities... something like
require('jxa-fs').writeFileSync('/tmp/hello.txt', 'Hello, world!')I've put up a module, jxapp — which lets you access the application instance using one line of code, and package up the alert, confirm, and prompt functions for immediate use — on npm, as an example. Let's use the keyword jxa for modules designed to be run on the JXA environment.
Another way is to read the file and eval() the read code. I have not tested speed, but I think the easiest way is to use the Objective-C bridge to read the contents of the file in with NSFileManager.
ObjC.import('Foundation');
var fm = $.NSFileManager.defaultManager;
var contents = fm.contentsAtPath(path.toString()); // NSData
contents = $.NSString.alloc.initWithDataEncoding(contents, $.NSUTF8StringEncoding);
eval(ObjC.unwrap(contents));Using open for access:
// Where app is any Application instance
app.includeStandardAdditions = true;
var handle = app.openForAccess(path); // an integer
var contents = app.read(handle);
app.closeHandle(handle);
eval(contents);Use this require() function, with this code at the top of your script. Any script that needs the function will need this boiler-plate as well. All this does is set up a local structure similar to what a node module might expect as part of the global namespace, then runs eval() with the code read via NSFileManager via the Objective-C bridge.
var require = function (path) {
if (typeof app === 'undefined') {
app = Application.currentApplication();
app.includeStandardAdditions = true;
}
var handle = app.openForAccess(path);
var contents = app.read(handle);
app.closeAccess(path);
var module = {exports: {}};
var exports = module.exports;
eval(contents);
return module.exports;
};JavaScript for Automation Pre-Processor jxapp is a small command-line utility written in Swift that extends JXA by enabling source files inclusion and conditional processing of code. The syntax is hopefully self-explanatory, here's what a jxapp-enabled script might look like:
#!/usr/local/bin/jxapp
//include-once 'argv.js'
//include-once 'print_and_log.js'
//include-once 'url.js'
//if-set DEBUG
//include-once debugLog.js
//fi
function run() {
try {
let selfUrl = URL.fileUrlWithPath(argv[0])
print(selfUrl.lastPathComponent)
} catch (error) {
//if-unset DEBUG
log(`Error: ${error}`)
// else
debugLog.write(`Error: ${error}`)
//fi
$.exit(1)
}
$.exit(0)
}A more detailed description is available on github.
- Foreword
- Conventions Used in This Cookbook
- Using JavaScript for Automation
- ES6 Features in JXA
- Getting the Application Instance
- User Interactions
- User Interaction with Files and Folders
- Using Objective-C (ObjC) with JXA
- Shell and CLI Interactions
- Importing Scripts
- iTunes
- Keynote
- Messages
- System Events
- Safari & Chrome
- Script Editor
- XML
- Examples