You know what the most-used preprocessor is on CodePen? TRICK QUESTION. It's one that all JavaScript is run through, no matter what. You can't choose (or un-choose) it. We refer to it as "instrumenting" JavaScript, and we do it for one main purpose: stopping long-running and infinite loops.

Before we did this, long-running and infinite loops were a pretty major problem on CodePen. You could just be innocently browsing around CodePen and have your browser lock up from someone else's Pen that had one. Or you could accidently type one into the JavaScript editor yourself and have the browser lock up before you could fix it.

To get around this, we instrument the JavaScript and insert a little bit of our own code into every loop in such a way that we can break the loop if it goes too long.

We do this conversion (and conversion back) because that's the only practical method for manipulating JavaScript. You wouldn't be manipulating it as a string, that would be weird and impractical.

After we've done our conversion, what started out as:

for (var i = 1; i++; i < 100) {

}

Gets converted to:

for (var i = 1; i++; i < 100) {
    if (window.CP.shouldStopExecution(1)) {
        break;
    }
}
window.CP.exitedLoop(1);

That's our little timer to check if the loop does indeed exit, and exit in a somewhat timely fashion.

In order to do this conversion, there are two major libraries involved:

  • esprima: Turns JavaScript into an Abstract Syntax Tree (AST)
  • escodegen: Turns the AST back into JavaScript

They are pretty wonderful libraries. They do the job, and quickly, the vast majority of the time. But as many of you know, JavaScript is evolving and there is new syntax stuff available (i.e. "ES6" and "ES7"). Occasionally, some of the new syntax trips up these libraries.

Here's two examples:

function returnHTML() {
  return `
    <h1>Hello</h1>
  `;
}

Will be incorrectly turned into:

function returnHTML() {
    return;
    `
    <h1>Hello</h1>
  `;
}

Another one:

const { a = 1, b = 2} = { a: 2 };

Will be incorrectly turned into:

const {a, b} = { a: 2 };

This is tricky on CodePen, because all JavaScript is run through this conversion process. We don't make it optional. At the moment, the gains of instrumenting JavaScript far outweigh the bugs. The libraries are pretty large, and we are hoping are updated to handle this new syntax stuff. We have a ticket open (I'd love to fix it myself, but it's little over my head). If it becomes even more problematic (definitely let us know when you run into it), we'll re-evaluate.