eslint-config-chain-able
Version:
an opinionated ESLint configuration
140 lines (121 loc) • 4.15 kB
JavaScript
// The algorithm used to determine whether a regexp can appear at a
// given point in the program is loosely based on sweet.js' approach.
// See https://github.com/mozilla/sweet.js/wiki/design
import {Parser} from "./state"
import {types as tt} from "./tokentype"
import {lineBreak} from "./whitespace"
export class TokContext {
constructor(token, isExpr, preserveSpace, override, generator) {
this.token = token
this.isExpr = !!isExpr
this.preserveSpace = !!preserveSpace
this.override = override
this.generator = !!generator
}
}
export const types = {
b_stat: new TokContext("{", false),
b_expr: new TokContext("{", true),
b_tmpl: new TokContext("${", true),
p_stat: new TokContext("(", false),
p_expr: new TokContext("(", true),
q_tmpl: new TokContext("`", true, true, p => p.readTmplToken()),
f_expr: new TokContext("function", true),
f_expr_gen: new TokContext("function", true, false, null, true),
f_gen: new TokContext("function", false, false, null, true)
}
const pp = Parser.prototype
pp.initialContext = function() {
return [types.b_stat]
}
pp.braceIsBlock = function(prevType) {
if (prevType === tt.colon) {
let parent = this.curContext()
if (parent === types.b_stat || parent === types.b_expr)
return !parent.isExpr
}
if (prevType === tt._return)
return lineBreak.test(this.input.slice(this.lastTokEnd, this.start))
if (prevType === tt._else || prevType === tt.semi || prevType === tt.eof || prevType === tt.parenR || prevType == tt.arrow)
return true
if (prevType == tt.braceL)
return this.curContext() === types.b_stat
return !this.exprAllowed
}
pp.inGeneratorContext = function() {
for (let i = this.context.length - 1; i >= 0; i--)
if (this.context[i].generator) return true
return false
}
pp.updateContext = function(prevType) {
let update, type = this.type
if (type.keyword && prevType == tt.dot)
this.exprAllowed = false
else if (update = type.updateContext)
update.call(this, prevType)
else
this.exprAllowed = type.beforeExpr
}
// Token-specific context update code
tt.parenR.updateContext = tt.braceR.updateContext = function() {
if (this.context.length == 1) {
this.exprAllowed = true
return
}
let out = this.context.pop(), cur
if (out === types.b_stat && (cur = this.curContext()) && cur.token === "function") {
this.context.pop()
this.exprAllowed = false
} else if (out === types.b_tmpl) {
this.exprAllowed = true
} else {
this.exprAllowed = !out.isExpr
}
}
tt.braceL.updateContext = function(prevType) {
this.context.push(this.braceIsBlock(prevType) ? types.b_stat : types.b_expr)
this.exprAllowed = true
}
tt.dollarBraceL.updateContext = function() {
this.context.push(types.b_tmpl)
this.exprAllowed = true
}
tt.parenL.updateContext = function(prevType) {
let statementParens = prevType === tt._if || prevType === tt._for || prevType === tt._with || prevType === tt._while
this.context.push(statementParens ? types.p_stat : types.p_expr)
this.exprAllowed = true
}
tt.incDec.updateContext = function() {
// tokExprAllowed stays unchanged
}
tt._function.updateContext = function(prevType) {
if (prevType.beforeExpr && prevType !== tt.semi && prevType !== tt._else &&
!((prevType === tt.colon || prevType === tt.braceL) && this.curContext() === types.b_stat))
this.context.push(types.f_expr)
this.exprAllowed = false
}
tt.backQuote.updateContext = function() {
if (this.curContext() === types.q_tmpl)
this.context.pop()
else
this.context.push(types.q_tmpl)
this.exprAllowed = false
}
tt.star.updateContext = function(prevType) {
if (prevType == tt._function) {
if (this.curContext() === types.f_expr)
this.context[this.context.length - 1] = types.f_expr_gen
else
this.context.push(types.f_gen)
}
this.exprAllowed = true
}
tt.name.updateContext = function(prevType) {
let allowed = false
if (this.options.ecmaVersion >= 6) {
if (this.value == "of" && !this.exprAllowed ||
this.value == "yield" && this.inGeneratorContext())
allowed = true
}
this.exprAllowed = allowed
}