Hacking Semicolons

@youyuxi

Core dev @meteorjs

Author @vuejs

Previously @ Google Creative Lab

Semicolon-less

JavaScript

For the love of god,

WHY???

Why do we have semicolons in the first place?

Because required semicolons makes compilers easier to write!

Why do we have semicolons in JavaScript?

"Because it's C-like"

JavaSript ASI: the legendary flame war

But JavaScript is not C or Java!

Auto Semicolon Insertion (ASI)

Languages with optional semicolons

(and with a convention to omit them):

  • Go
  • Scala
  • Ruby
  • Python
  • Swift
  • Groovy
  • ... more

Optional semicolon is a viable style because:

  • Compilers today are smart enough to handle multi-line statements.
     
  • We programmers can also easily recognize EOL as end of statement vs. multi-line statements, via proper whitespace formatting.

Non-trivial projects using semicolon-less style:

(3k+ stars on GitHub)

Understanding ASI

ECMA-262 - Edition 5.1 - Section 7.9

TL;DR

A semicolon is inserted when:

  1. The parser finds a token that is not allowed by the formal grammar, AND
  2. There is a line break or a closing brace at that point.
  3. Restricted productions (explained later)
// explicit termination
42;"hello"

// auto insertion, because
// `42 "hello"` is not valid statement
42\n"hello"
// explicit termination
a = 1;
b = 2;

// auto insertion, because
// `a = 1 b = 2` is not a valid statement
a = 1
b = 2

Restricted productions

postfix ++ or --

return

break

continue

ES6 yield

If a line break is found after one of these tokens,

a semicolon will be inserted regardless of what follows.

// auto insertion!
// because `return` is one of the "restricted productions"
return
{
  a: 1
}

// this works as expected
return {
  a: 1
}

But isn't this super hard to remember???

Simple rules to remember

Add a leading semicolon when a line starts with one of the following:

+ - [ ( /

The restricted productions can bite you

regardless of whether you write semicolons or not,

so you need to understand it anyway.

// treated as plus operation
a = b
+a

// treated as minus operation
a = b
-a

// treated as division operation
a = b
/something/.test(a)

// treated as function invocation
a = b
(function () {})()

// treated as property access
a = b
[1, 2, 3].forEach()

In practice: just [ and (

Let's compare it with the overhead of

"Just add semicolons"

var a = function () {
  /* ... */
};

// VS.

function a () {
  /* ... */
}

// If you forgot the semicolon...
var a = function () {
  /* ... */
}
// uh oh!
[1, 2, 3].forEach();
if () {
} // no semicolon

for () {
} // no semicolon

while () {
} // no semicolon

// --- VS. ---

var a = function () {
}; // need semicolon

var a = {
  prop: value
}; // need semicolon

Whenever you encounter a closing brace,

you need to do a lookbehind to decide whether a semicolon is needed there.

Enough!

I write semicolons because I like them!

(That's totally fine, there are many other good reasons too)

SEMI

A tool to automatically convert between

with/without semicolon styles.

Requirements:

  • Two-way conversion
  • Preserve original whitespace formatting
  • Handle edge cases

First attempt: Recast

Unfortunately recast is about pure AST transform and doesn't expose the source.

Second attempt: JSHint!

Works, but is a big hack

Now: ESLint!

THANKS

Slide available at:

https://slides.com/evanyou/semicolons

Hacking Semicolons

By Evan You

Hacking Semicolons

  • 27,233