I am playing with some ideas to expand my use of JXA for scripting.
The very brief script below is intended simply to explore how I can assign names and other metadata to a file in DT3.
I was hoping this example would reassign the filename of the first item in the list of selected items. Script Editor gives me “Error -2700 Script Error.” Keyboard Maestro give the error that I am assigning to the incorrect type.
The JavaScript interpreter has a global namespace which persists between runs.
Once you have defined names in it, the definition will still be there on your next script run. Potentially creating confusion.
var is now deprecated (let is less complex and tricky, and now preferred), but neigher var nor let avoid the problem here of polluting the global namespace.
(names and app will retain those definitions in the global namespace after your script is over, if you use let/var, and could prove confusing later)
The trick is to wrap your code in its own local and temporary name-space, protecting the global definitions, and protecting subsequent script runs.
You can do this either with an IIFE, which has the advantage of working in any JavaScript context, and is a good habit to learn:
@winter has already explained the const issue. That’s why I always use an anonymous self-executing function to run my JS code
Also, your assignment will not change the name of the first record to “test”. Here’s what happens:
app.selectedRecords gets an array of Object Specifiers, namely the selected records
app.selectedRecords.name gets an array of Object Specifiers, namely the names of the selected records
app.selectedRecords.name() does a get on these names, which results in an array of Strings.
Now you assign a new value to the first element of this array, which works fine. But it will not change the name of the first record because the first element of the array of Strings will not have any connection to the records. It does not contain references (or Object Specifiers), but values. So to say.
What would work is app.selectedRecords[0].name = "test"
because there name is an Object Specifier, and assigning to it is equivalent to a set operation.
The problem here is that JXA uses confusing syntax: () gets a value, and assignment to the Object Specifiers sets its value. If they’d used set and get, it would be clearer.
I’d use addCustomMetadata because I know that it works. But that’s not a direct answer to your question. What you could try if you want to change the property directly:
Attempt at an explanation: app.selectedRecords[0].customMetadata is an Object Specifier, but app.selectedRecords[0].customMetadata['md…'] is not, but only a string (or number or whatever). JXA is probably getting confused because everything kind of “looks” like an Object Specifier, and then it tries to call its set method, which doesn’t work. Sounds a bit far-fetched, perhaps.
Typo. The property is selectedRecords, and case is relevant in JavaScript.
If you run the script in Script Editor, you’ll immediately spot these things (not because the program is so good, but:) You can have it display the Apple Events it sends and receives, and then you’ll see that selectedrecords[0] is passed as null:
If it were a good program, it would immediately tell you that app doesn’t have a property selectedrecords when “compiling”. But, helas … it’s not even compiling anything but just running a basic syntax check.
Is there any alternative IDE/compiler that can be used for JXA instead of Script Editor and which is more informative? WebStorm maybe - or is that overkill just for scripts?
AFAIK: Not. The main issue here is (in my opinion) that Apple does not provide for introspection of its objects. They are just black boxes. While you can step through a JXA script in Safari (add debugger at the point where you want Safari to take over), it is of very little help for anything related to application scripting (for the reason mentioned before).
You’d see the same problem with WebStorm (which would not be able to run your scripts because it doesn’t know how to call into OSA!). So, I use plain old config.log to “debug” my code.
Only for the JS code outside of application scripting, since even Safari does not provide any introspection for application objects, like DT records. So, it’s really quite limited. Running the script in Script Editor can sometimes provide more insight, since you can follow the Apple events and responses there.