// Requirements
//------------------------------------------------------------------------------
-const lodash = require("lodash");
-const astUtils = require("./utils/ast-utils");
const createTree = require("functional-red-black-tree");
+const astUtils = require("./utils/ast-utils");
+
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
"BreakStatement",
"CallExpression",
"CatchClause",
+ "ChainExpression",
"ClassBody",
"ClassDeclaration",
"ClassExpression",
]
},
outerIIFEBody: {
- type: "integer",
- minimum: 0
+ oneOf: [
+ {
+ type: "integer",
+ minimum: 0
+ },
+ {
+ enum: ["off"]
+ }
+ ]
},
MemberExpression: {
oneOf: [
type: "boolean",
default: false
},
+ offsetTernaryExpressions: {
+ type: "boolean",
+ default: false
+ },
ignoredNodes: {
type: "array",
items: {
parameterParens.add(openingParen);
parameterParens.add(closingParen);
- offsets.setDesiredOffset(openingParen, sourceCode.getTokenBefore(openingParen), 0);
+
+ /*
+ * If `?.` token exists, set desired offset for that.
+ * This logic is copied from `MemberExpression`'s.
+ */
+ if (node.optional) {
+ const dotToken = sourceCode.getTokenAfter(node.callee, astUtils.isQuestionDotToken);
+ const calleeParenCount = sourceCode.getTokensBetween(node.callee, dotToken, { filter: astUtils.isClosingParenToken }).length;
+ const firstTokenOfCallee = calleeParenCount
+ ? sourceCode.getTokenBefore(node.callee, { skip: calleeParenCount - 1 })
+ : sourceCode.getFirstToken(node.callee);
+ const lastTokenOfCallee = sourceCode.getTokenBefore(dotToken);
+ const offsetBase = lastTokenOfCallee.loc.end.line === openingParen.loc.start.line
+ ? lastTokenOfCallee
+ : firstTokenOfCallee;
+
+ offsets.setDesiredOffset(dotToken, offsetBase, 1);
+ }
+
+ const offsetAfterToken = node.callee.type === "TaggedTemplateExpression" ? sourceCode.getFirstToken(node.callee.quasi) : openingParen;
+ const offsetToken = sourceCode.getTokenBefore(offsetAfterToken);
+
+ offsets.setDesiredOffset(openingParen, offsetToken, 0);
addElementListIndent(node.arguments, openingParen, closingParen, options.CallExpression.arguments);
}
const baseOffsetListeners = {
"ArrayExpression, ArrayPattern"(node) {
const openingBracket = sourceCode.getFirstToken(node);
- const closingBracket = sourceCode.getTokenAfter(lodash.findLast(node.elements) || openingBracket, astUtils.isClosingBracketToken);
+ const closingBracket = sourceCode.getTokenAfter([...node.elements].reverse().find(_ => _) || openingBracket, astUtils.isClosingBracketToken);
addElementListIndent(node.elements, openingBracket, closingBracket, options.ArrayExpression);
},
},
ArrowFunctionExpression(node) {
- const firstToken = sourceCode.getFirstToken(node);
+ const maybeOpeningParen = sourceCode.getFirstToken(node, { skip: node.async ? 1 : 0 });
- if (astUtils.isOpeningParenToken(firstToken)) {
- const openingParen = firstToken;
+ if (astUtils.isOpeningParenToken(maybeOpeningParen)) {
+ const openingParen = maybeOpeningParen;
const closingParen = sourceCode.getTokenBefore(node.body, astUtils.isClosingParenToken);
parameterParens.add(openingParen);
parameterParens.add(closingParen);
addElementListIndent(node.params, openingParen, closingParen, options.FunctionExpression.parameters);
}
+
addBlocklessNodeIndent(node.body);
},
},
"BlockStatement, ClassBody"(node) {
-
let blockIndentLevel;
if (node.parent && isOuterIIFE(node.parent)) {
if (!astUtils.STATEMENT_LIST_PARENTS.has(node.parent.type)) {
offsets.setDesiredOffset(sourceCode.getFirstToken(node), sourceCode.getFirstToken(node.parent), 0);
}
+
addElementListIndent(node.body, sourceCode.getFirstToken(node), sourceCode.getLastToken(node), blockIndentLevel);
},
offsets.setDesiredOffset(questionMarkToken, firstToken, 1);
offsets.setDesiredOffset(colonToken, firstToken, 1);
- offsets.setDesiredOffset(firstConsequentToken, firstToken, 1);
+ offsets.setDesiredOffset(firstConsequentToken, firstToken, firstConsequentToken.type === "Punctuator" &&
+ options.offsetTernaryExpressions ? 2 : 1);
/*
* The alternate and the consequent should usually have the same indentation.
* If `baz` were aligned with `bar` rather than being offset by 1 from `foo`, `baz` would end up
* having no expected indentation.
*/
- offsets.setDesiredOffset(firstAlternateToken, firstToken, 1);
+ offsets.setDesiredOffset(firstAlternateToken, firstToken, firstAlternateToken.type === "Punctuator" &&
+ options.offsetTernaryExpressions ? 2 : 1);
}
}
},
* 2. Don't set any offsets against the first token of the node.
* 3. Call `ignoreNode` on the node sometime after exiting it and before validating offsets.
*/
- const offsetListeners = lodash.mapValues(
- baseOffsetListeners,
+ const offsetListeners = {};
+
+ for (const [selector, listener] of Object.entries(baseOffsetListeners)) {
/*
* Offset listener calls are deferred until traversal is finished, and are called as
* To avoid this, the `Identifier` listener isn't called until traversal finishes and all
* ignored nodes are known.
*/
- listener =>
- node =>
- listenerCallQueue.push({ listener, node })
- );
+ offsetListeners[selector] = node => listenerCallQueue.push({ listener, node });
+ }
// For each ignored node selector, set up a listener to collect it into the `ignoredNodes` set.
const ignoredNodes = new Set();