4

I use JXA to script workflows for Alfred 2 and recently tried to run a script from within another script. I need to pass some text between the scripts, so I decided to use parameters, but whenever I try to pass a string, a number, an array or anything else that isn't an object to it, it gives the error "Error on line 4: Error: An error occurred.". If I do pass an object, the second script (the one being run by the first script) receives an empty object rather than the one passed to it. The same happens when the first script is an AppleScript, but if the second script is an AppleScript, it all works perfectly. Passing arguments through osascript from the command line also works. Is the API broken or is there something that I'm doing wrong?

First script:

var app = Application.currentApplication();
app.includeStandardAdditions = true;

app.runScript(new Path("/path/to/second/script.scpt"), { withParameters: "Hello World!" });

Second script:

function run(args) {
    return args;
}

Edit:

If the second script is edited as below, the dialogue is displayed but the runScript method of the first script still returns an error.

function run(args) {
    var app = Application.currentApplication();
    app.includeStandardAdditions = true;

    app.displayDialog(args.toString());

    return args;
}

Edit 2:

The runScript function actually seems to be working perfectly other than the problem with the parameters. The error isn't actually being thrown, just displayed by the Script Editor, and execution continues after the call to runScript as if nothing had happened. The returned value also work perfectly, despite the parameters not working.

A note about Alfred 2 workflows

To run some code in Alfred 2 (triggered by a search, keyboard command, etc.), it must be typed into a box in the app, not in a file. The box to enter code in is very small and does not provide syntax highlighting, and this makes editing code difficult and annoying. For smaller files, it is okay, but for larger files it is easier to use a short script to run a script file. I've tried Bash, which would be the simplest option, but Alfred 2 does not provide an option to escape single quotes. I also cannot use script libraries (to my knowledge, correct me if I'm wrong), as the code is not in a script bundle and all of the required files need to be within the same folder (for exportation reasons).

Ontonator
  • 319
  • 3
  • 17
  • `if the second script is an AppleScript, it all works perfectly. Passing arguments through osascript from the command line also works. Is the API broken or is there something that I'm doing wrong?` -- Great question. – JMichaelTX Jan 30 '16 at 10:16
  • The reason why you can't pass parameters to JXA scripts is that the OS X API for invoking such scripts, NSUserScriptTask, does not provide a way to pass parameters. As long as Apple doesn't add that ability, other scripting code, such as invoking another script from a script, can't do that, either. Why Apple isn't adding that ability is beyond me, though. – Thomas Tempelmann Apr 22 '16 at 13:35
  • I get a slightly different behavior in Mojave. If I pass {withParameters:"Hello World"} to the second script, the second script does receive "Hello World" in args. However, "received = app.runScript(...)" throws an "Error: An error occurred", and received doesn't get anything. If I don't care about the return value from the second script, I can just put runScript in a try catch, ignore the error, and move along. The first script can still pass a string to the second script. The second script can't return something to the first script. – jl303 Mar 01 '19 at 16:42

1 Answers1

0

I don't know how to avoid the runScript error, but I can suggest an alternative approach: load the script as a script library.

Using a script library

Turning a script into a library can be as simple as saving the script to ~/Library/Script Libraries. If your script file is named script.scpt and has a run handler, and you save it to the Script Libraries folder, then you can then invoke it from another script like so:

Library("script").run(["Hello, world!"])

Script libraries are documented in the JXA release notes for OS X 10.10, in the WWDC 2014 session video introducing JXA, and in the AppleScript Language Guide.

Embedding a script library inside of a script bundle

According to the AppleScript Language Guide documentation for script libraries, there is a search policy for finding Script Libraries folders. The first place it searches is:

If the script that references the library is a bundle, the script’s bundle Resources directory. This means that scripts may be packaged and distributed with the libraries they use.

To apply this to the example given in the question, you would need to re-save the first script as a script bundle, and then embed the second script inside of the first script.

For example, if you re-save the first script as script.scptd, then you could save the second script embedded.scpt to script.scptd/Resources/Script Libraries/embedded.scpt. You should then be able to use Library('embedded') to access the script library.

To re-save an existing script as a script bundle, you can either use the File > Export... menu item in Script Editor, or you can hold down option while selecting the File menu to reveal the File > Save As... menu item. The File Format pop-up menu lets you choose the Script bundle format.

Once you have a script bundle open, you can reveal the bundle content panel by using the Show Bundle Contents menu item or toolbar button. You can then use the gear menu to create the Script Libraries folder inside of the Resources folder, and then you can drag a script into that folder.

bacongravy
  • 893
  • 8
  • 13
  • This is not ideal as the Alfred 2 workflow should contain all the required files within the designated workflow folder, as this allows it to be distributed as a single file which installs itself. If there is a script in the `Script Libraries` folder then the end user will have to manually install this file as well. Is there a way to load any script (not just ones within the `Script Libraries` folder) as a `Library` object? – Ontonator Jan 30 '16 at 00:08
  • That constraint wasn't clear from the original question, since you used an absolute placeholder path ("/path/to/second/script.scpt") in your example, rather than a path relative to the first script. I've updated my answer to describe how to embed a script library inside another script so that they can be distributed together. I think this should make it work better as an Alfred 2 workflow. – bacongravy Jan 30 '16 at 20:07
  • That would not be possible, as script 1 is not saved as a file, but entered in a dialogue in Alfred 2. I've added an explanation to the question. – Ontonator Jan 31 '16 at 00:25