To make semicolon rules simple
Every line that begins with a (
, [
, `, or any operator (/, +, - are the only valid ones), must begin with a semicolon to be interpreted as its own line. All other line breaks are implicit.
That's it. Done.
Why?
Consider the following:
func()
;[0].concat(myarr).forEach(func)
;(myarr).forEach(func)
;`hello`.forEach(func)
;/hello/.exec(str)
;+0
;-0
Following the above rules prevent the above from being interpreted as
func()[0].concat(myarr).forEach(func)(myarr).forEach(func)`hello`.forEach(func)/hello/.forEach(func)+0-0
Additional Notes
To mention what will happen: brackets will index, parentheses will be treated as function parameters. The backtick would transform into a tagged template, regex will turn into division, and explicitly +/- signed integers will turn into plus/minus operators.
Of course, you can avoid this by just adding a semicolon to the end of every linebreak, but even doing that won't always help you, since when you don't end a line with a semicolon, it might implicitly add one on your behalf. So keep in mind statements like
return // Implicit semicolon, will return undefined.
(1+2);
i // Implicit semicolon on this line
++; // But, if you intended "i++;" and you wrote it like this,
// you need help.
The above case will happen to return/continue/break/++/--. Any linter will catch this with dead-code or ++/-- syntax error (++/-- will never realistically happen).
Finally, if you want file concatenation to work, make sure each file ends with a semicolon. If you're using a bundler program (recommended), it should do this automatically.