JavaScript variable scoping and Drafts

An interesting problem came up in the user forum for Drafts yesterday. User trbielec was making new actions by stringing together other actions that had already been written, an admirable attempt to use the DRY principle. But he was getting error messages about duplicate variable names. This is a rare case in which a scripting problem is related to honest-to-goodness computer science rather than just an error in logic.

A simple example that demonstrates the problem can be built from two actions, which you can install from the links:

Subroutine Action consists of a single JavaScript step with this code:

javascript:
let a = "Subroutine";
alert("Greetings from the " + a);

Running this will cause an alert to appear.

Subroutine Action alert

Caller Action has two steps. The first is a JavaScript step with similar code,

javascript:
let a = "Caller";
alert("Greetings from the " + a);

and the second step is an Include Action step that calls Subroutine Action. When this is run, a “Greetings from the Caller” alert appears as expected,

Caller Action alert

but instead of then seeing the “Greetings from Subroutine” alert, you’ll get an error message saying you can’t create a duplicate variable a.

Variable redefinition error message

The error comes from JavaScript’s variable scoping rules. JavaScript doesn’t allow you to use let to define a variable twice within the same code block. You might think that because the two let statements are in separate actions, they are also in separate blocks, but as trbielec learned, that isn’t the case. Drafts treats all the JavaScript code in an action—even if some of that code is there through an Include Action step—as one big chunk. So in our example, it’s as if we had written

javascript:
let a = "Subroutine";
alert("Greetings from the " + a);
let a = "Caller";
alert("Greetings from the " + a);

which is clearly illegal.

The solution is pretty simple. Just wrap one (or both) of the two bits of JavaScript in braces:

javascript:
{
let a = "Subroutine";
alert("Greetings from the " + a);
}

The braces define a block, so now the a in Subroutine Action is different from the a in Caller Action and there’s no conflict and no error. Caller Action runs the way you expect, with both alerts appearing in sequence.

I’ve never had a scoping conflict like this in my Drafts actions, and more significantly, Greg Pierce says he’s never seen it arise in the Drafts forums before, so it’s unlikely you will ever put this blog post to practical use. But it was a fun way to divert myself from the work I should be doing.