Add isNonEmptyArray (#9902)

This commit is contained in:
fisker Cheung authored and GitHub committed 2020-12-17 14:46:59 +08:00
1 parent 0fd2a28cff
commit c7e354e38c
23 files changed
+215 -48

No files matched your search

+1
View File
@@ -109,6 +109,7 @@ overrides:
- files: src/**/*.js
rules:
prettier-internal-rules/prefer-fast-path-each: error
prettier-internal-rules/prefer-is-non-empty-array: error
- files:
- src/language-*/**/*.js
rules:
@@ -7,6 +7,7 @@ module.exports = {
"jsx-identifier-case": require("./jsx-identifier-case"),
"no-node-comments": require("./no-node-comments"),
"prefer-fast-path-each": require("./prefer-fast-path-each"),
"prefer-is-non-empty-array": require("./prefer-is-non-empty-array"),
"require-json-extensions": require("./require-json-extensions"),
},
};
@@ -0,0 +1,152 @@
"use strict";
const getLengthSelector = (path) =>
`[${path}.type="MemberExpression"][${path}.property.type="Identifier"][${path}.property.name="length"]`;
const selector = [
"LogicalExpression",
':not(FunctionDeclaration[id.name="isNonEmptyArray"] *)',
'[operator="&&"]',
`:matches(${[
// `&& foo.length`
getLengthSelector("right"),
// `&& foo.length !== 0`
// `&& foo.length > 0`
[
'[right.type="BinaryExpression"]',
':matches([right.operator="!=="], [right.operator=">"])',
getLengthSelector("right.left"),
'[right.right.type="Literal"]',
'[right.right.raw="0"]',
].join(""),
].join(", ")})`,
].join("");
const negativeSelector = [
"LogicalExpression",
'[operator="||"]',
`:matches(${[
// `|| !foo.length`
[
'[right.type="UnaryExpression"]',
'[right.operator="!"]',
getLengthSelector("right.argument"),
].join(""),
// `|| foo.length === 0`
[
'[right.type="BinaryExpression"]',
'[right.operator="==="]',
getLengthSelector("right.left"),
'[right.right.type="Literal"]',
'[right.right.raw="0"]',
].join(""),
].join(", ")})`,
].join("");
const isArrayIsArrayCall = (node) =>
node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
node.callee.object.type === "Identifier" &&
node.callee.object.name === "Array" &&
node.callee.property.type === "Identifier" &&
node.callee.property.name === "isArray";
const MESSAGE_ID = "prefer-is-non-empty-array";
module.exports = {
meta: {
type: "suggestion",
docs: {
url:
"https://github.com/prettier/prettier/blob/master/scripts/eslint-plugin-prettier-internal-rules/prefer-is-non-empty-array.js",
},
messages: {
[MESSAGE_ID]: "Please use `isNonEmptyArray()`.",
},
fixable: "code",
},
create(context) {
const sourceCode = context.getSourceCode();
return {
[selector](node) {
let { left, right } = node;
while (left.type === "LogicalExpression" && left.operator === "&&") {
left = left.right;
}
let leftObject = left;
// `Array.isArray(foo)`
if (isArrayIsArrayCall(leftObject)) {
leftObject = leftObject.arguments[0];
}
const rightObject =
right.type === "BinaryExpression" ? right.left.object : right.object;
const objectText = sourceCode.getText(rightObject);
// Simple compare with code
if (sourceCode.getText(leftObject) !== objectText) {
return;
}
const [start] = left.range;
const [, end] = node.range;
context.report({
loc: {
start: sourceCode.getLocFromIndex(start),
end: sourceCode.getLocFromIndex(end),
},
messageId: MESSAGE_ID,
fix(fixer) {
return fixer.replaceTextRange(
[start, end],
`isNonEmptyArray(${objectText})`
);
},
});
},
[negativeSelector](node) {
let { left, right } = node;
while (left.type === "LogicalExpression" && left.operator === "||") {
left = left.right;
}
if (!left.type === "UnaryExpression" || left.operator !== "!") {
return;
}
const rightObject =
right.type === "UnaryExpression"
? right.argument.object
: right.left.object;
let leftObject = left.argument;
if (isArrayIsArrayCall(leftObject)) {
leftObject = leftObject.arguments[0];
}
const objectText = sourceCode.getText(rightObject);
// Simple compare with code
if (sourceCode.getText(leftObject) !== objectText) {
return;
}
const [start] = left.range;
const [, end] = node.range;
context.report({
loc: {
start: sourceCode.getLocFromIndex(start),
end: sourceCode.getLocFromIndex(end),
},
messageId: MESSAGE_ID,
fix(fixer) {
return fixer.replaceTextRange(
[start, end],
`!isNonEmptyArray(${objectText})`
);
},
});
},
};
},
};
+5
View File
@@ -624,6 +624,10 @@ function getShebang(text) {
return text.slice(0, index);
}
function isNonEmptyArray(object) {
return Array.isArray(object) && object.length > 0;
}
module.exports = {
inferParserByLanguage,
replaceEndOfLineWith,
@@ -660,4 +664,5 @@ module.exports = {
addTrailingComment,
isFrontMatterNode,
getShebang,
isNonEmptyArray,
};
+2 -1
View File
@@ -6,6 +6,7 @@ const {
hasNewline,
isFrontMatterNode,
isNextLineEmpty,
isNonEmptyArray,
} = require("../common/util");
const {
builders: {
@@ -469,7 +470,7 @@ function genericPrint(path, options, print) {
case "selector-pseudo": {
return concat([
maybeToLowerCase(node.value),
node.nodes && node.nodes.length > 0
isNonEmptyArray(node.nodes)
? concat(["(", join(", ", path.map(print, "nodes")), ")"])
: "",
]);
+3 -2
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../common/util");
const colorAdjusterFunctions = new Set([
"red",
"green",
@@ -59,7 +60,7 @@ function getPropOfDeclNode(path) {
}
function hasSCSSInterpolation(groupList) {
if (groupList && groupList.length) {
if (isNonEmptyArray(groupList)) {
for (let i = groupList.length - 1; i > 0; i--) {
// If we find `#{`, return true.
if (
@@ -76,7 +77,7 @@ function hasSCSSInterpolation(groupList) {
}
function hasStringOrFunction(groupList) {
if (groupList && groupList.length) {
if (isNonEmptyArray(groupList)) {
for (let i = 0; i < groupList.length; i++) {
if (groupList[i].type === "string" || groupList[i].type === "func") {
return true;
+3 -3
View File
@@ -3,7 +3,7 @@
const {
builders: { concat, join, hardline, line, softline, group, indent, ifBreak },
} = require("../document");
const { isNextLineEmpty } = require("../common/util");
const { isNextLineEmpty, isNonEmptyArray } = require("../common/util");
const { insertPragma } = require("./pragma");
const { locStart, locEnd } = require("./loc");
@@ -39,7 +39,7 @@ function genericPrint(path, options, print) {
return concat([
hasOperation ? n.operation : "",
hasOperation && hasName ? concat([" ", path.call(print, "name")]) : "",
n.variableDefinitions && n.variableDefinitions.length
isNonEmptyArray(n.variableDefinitions)
? group(
concat([
"(",
@@ -66,7 +66,7 @@ function genericPrint(path, options, print) {
return concat([
"fragment ",
path.call(print, "name"),
n.variableDefinitions && n.variableDefinitions.length
isNonEmptyArray(n.variableDefinitions)
? group(
concat([
"(",
+3 -4
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../common/util");
const NODES_KEYS = {
attrs: true,
children: true,
@@ -71,13 +72,11 @@ class Node {
}
get firstChild() {
return this.children && this.children.length !== 0
? this.children[0]
: null;
return isNonEmptyArray(this.children) ? this.children[0] : null;
}
get lastChild() {
return this.children && this.children.length !== 0
return isNonEmptyArray(this.children)
? this.children[this.children.length - 1]
: null;
}
+2 -2
View File
@@ -18,7 +18,7 @@ const {
},
utils: { mapDoc, cleanDoc },
} = require("../document");
const { replaceEndOfLineWith } = require("../common/util");
const { replaceEndOfLineWith, isNonEmptyArray } = require("../common/util");
const { print: printFrontMatter } = require("../utils/front-matter");
const clean = require("./clean");
const {
@@ -657,7 +657,7 @@ function getNodeContent(node, options) {
function printAttributes(path, options, print) {
const node = path.getValue();
if (!node.attrs || node.attrs.length === 0) {
if (!isNonEmptyArray(node.attrs)) {
return node.isSelfClosing
? /**
* <br />
+3 -3
View File
@@ -10,6 +10,7 @@ const {
addTrailingComment,
addDanglingComment,
getNextNonSpaceNonCommentCharacterIndex,
isNonEmptyArray,
} = require("../common/util");
const {
isBlockComment,
@@ -386,8 +387,7 @@ function handleClassComments({
enclosingNode.type === "TSInterfaceDeclaration")
) {
if (
enclosingNode.decorators &&
enclosingNode.decorators.length > 0 &&
isNonEmptyArray(enclosingNode.decorators) &&
!(followingNode && followingNode.type === "Decorator")
) {
addTrailingComment(
@@ -924,7 +924,7 @@ function getCommentChildNodes(node, options) {
node.value.type === "FunctionExpression" &&
getFunctionParameters(node.value).length === 0 &&
!node.value.returnType &&
(!node.value.typeParameters || node.value.typeParameters.length === 0) &&
!isNonEmptyArray(node.value.typeParameters) &&
node.value.body
) {
return [...(node.decorators || []), node.key, node.value.body];
+2 -1
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../../common/util");
const {
builders: { indent, hardline, softline, concat },
utils: { mapDoc, replaceNewlinesWithLiterallines, cleanDoc },
@@ -52,7 +53,7 @@ function transformCssDoc(quasisDoc, parentNode, expressionDocs) {
// returns a new doc with all the placeholders replaced,
// or null if it couldn't replace any expression
function replacePlaceholders(quasisDoc, expressionDocs) {
if (!expressionDocs || !expressionDocs.length) {
if (!isNonEmptyArray(expressionDocs)) {
return quasisDoc;
}
let replaceCounter = 0;
+3 -1
View File
@@ -1,5 +1,7 @@
"use strict";
const { isNonEmptyArray } = require("../common/util");
/**
* @typedef {import("./types/estree").Node} Node
*/
@@ -12,7 +14,7 @@ function locStart(node, opts) {
const decorators =
(node.declaration && node.declaration.decorators) || node.decorators;
if (decorators && decorators.length > 0) {
if (isNonEmptyArray(decorators)) {
return locStart(decorators[0]);
}
}
+2 -2
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../../common/util");
const {
builders: { concat, line, group, indent },
} = require("../../document");
@@ -69,8 +70,7 @@ function printAssignmentRight(leftNode, rightNode, printedRight, options) {
!shouldInlineLogicalExpression(rightNode.test)) ||
rightNode.type === "StringLiteralTypeAnnotation" ||
(rightNode.type === "ClassExpression" &&
rightNode.decorators &&
rightNode.decorators.length) ||
isNonEmptyArray(rightNode.decorators)) ||
((leftNode.type === "Identifier" ||
isStringLiteral(leftNode) ||
leftNode.type === "MemberExpression") &&
+2 -4
View File
@@ -1,7 +1,7 @@
"use strict";
const { printDanglingComments } = require("../../main/comments");
const { isNextLineEmpty } = require("../../common/util");
const { isNextLineEmpty, isNonEmptyArray } = require("../../common/util");
const {
builders: { concat, hardline, indent },
} = require("../../document");
@@ -21,7 +21,6 @@ function printBlock(path, options, print) {
}
parts.push("{");
const printed = printBlockBody(path, options, print);
if (printed) {
parts.push(indent(concat([hardline, printed])), hardline);
@@ -58,8 +57,7 @@ function printBlock(path, options, print) {
function printBlockBody(path, options, print) {
const node = path.getValue();
const nodeHasDirectives =
Array.isArray(node.directives) && node.directives.length > 0;
const nodeHasDirectives = isNonEmptyArray(node.directives);
const nodeHasBody = node.body.some((node) => node.type !== "EmptyStatement");
const nodeHasComment = hasComment(node, CommentCheckFlags.Dangling);
+7 -6
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../../common/util");
const { printComments, printDanglingComments } = require("../../main/comments");
const {
builders: { concat, join, line, hardline, softline, group, indent, ifBreak },
@@ -35,9 +36,9 @@ function printClass(path, options, print) {
const groupMode =
(n.id && hasComment(n.id, CommentCheckFlags.Trailing)) ||
(n.superClass && hasComment(n.superClass)) ||
(n.extends && n.extends.length !== 0) || // DeclareClass
(n.mixins && n.mixins.length !== 0) ||
(n.implements && n.implements.length !== 0);
isNonEmptyArray(n.extends) || // DeclareClass
isNonEmptyArray(n.mixins) ||
isNonEmptyArray(n.implements);
const partsGroup = [];
const extendsParts = [];
@@ -113,7 +114,7 @@ function shouldIndentOnlyHeritageClauses(node) {
function printList(path, options, print, listName) {
const n = path.getValue();
if (!n[listName] || n[listName].length === 0) {
if (!isNonEmptyArray(n[listName])) {
return "";
}
@@ -158,7 +159,7 @@ function printClassMethod(path, options, print) {
const n = path.getValue();
const parts = [];
if (n.decorators && n.decorators.length !== 0) {
if (isNonEmptyArray(n.decorators)) {
parts.push(printDecorators(path, options, print));
}
if (n.accessibility) {
@@ -197,7 +198,7 @@ function printClassProperty(path, options, print) {
const parts = [];
const semi = options.semi ? ";" : "";
if (n.decorators && n.decorators.length !== 0) {
if (isNonEmptyArray(n.decorators)) {
parts.push(printDecorators(path, options, print));
}
if (n.accessibility) {
+3 -2
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../../common/util");
const {
builders: { concat, join, line, group, indent, ifBreak },
} = require("../../document");
@@ -41,7 +42,7 @@ function printInterface(path, options, print) {
CommentCheckFlags.Trailing | CommentCheckFlags.Line
);
if (n.extends && n.extends.length !== 0) {
if (isNonEmptyArray(n.extends)) {
extendsParts.push(
shouldIndentOnlyHeritageClauses
? ifBreak(" ", line, {
@@ -57,7 +58,7 @@ function printInterface(path, options, print) {
if (
(n.id && hasComment(n.id, CommentCheckFlags.Trailing)) ||
(n.extends && n.extends.length !== 0)
isNonEmptyArray(n.extends)
) {
const printedExtends = concat(extendsParts);
if (shouldIndentOnlyHeritageClauses) {
+2 -1
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../../common/util");
const {
builders: { concat, indent, join, line },
} = require("../../document");
@@ -67,7 +68,7 @@ function printBindExpressionCallee(path, options, print) {
function printTypeScriptModifiers(path, options, print) {
const n = path.getValue();
if (!n.modifiers || !n.modifiers.length) {
if (!isNonEmptyArray(n.modifiers)) {
return "";
}
return concat([join(" ", path.map(print, "modifiers")), " "]);
+4 -3
View File
@@ -1,5 +1,6 @@
"use strict";
const { isNonEmptyArray } = require("../../common/util");
const {
builders: { concat, softline, group, indent, join, line, ifBreak, hardline },
} = require("../../document");
@@ -168,7 +169,7 @@ function printModuleSpecifiers(path, options, print) {
/** @type{Doc[]} */
const parts = [" "];
if (node.specifiers && node.specifiers.length > 0) {
if (isNonEmptyArray(node.specifiers)) {
const standaloneSpecifiers = [];
const groupedSpecifiers = [];
@@ -246,7 +247,7 @@ function shouldNotPrintSpecifiers(node, options) {
if (
type !== "ImportDeclaration" ||
(Array.isArray(specifiers) && specifiers.length > 0) ||
isNonEmptyArray(specifiers) ||
importKind === "type"
) {
return false;
@@ -260,7 +261,7 @@ function shouldNotPrintSpecifiers(node, options) {
function printImportAssertions(path, options, print) {
const node = path.getNode();
if (Array.isArray(node.assertions) && node.assertions.length !== 0) {
if (isNonEmptyArray(node.assertions)) {
return concat([
" assert {",
options.bracketSpacing ? " " : "",
+3 -4
View File
@@ -12,6 +12,7 @@ const {
printString,
printNumber,
isNextLineEmpty,
isNonEmptyArray,
} = require("../common/util");
const {
builders: {
@@ -127,8 +128,7 @@ function genericPrint(path, options, printPath, args) {
) {
// their decorators are handled themselves
} else if (
node.decorators &&
node.decorators.length > 0 &&
isNonEmptyArray(node.decorators) &&
// If the parent node is an export declaration and the decorator
// was written before the export, the export will be responsible
// for printing the decorators.
@@ -162,8 +162,7 @@ function genericPrint(path, options, printPath, args) {
} else if (
isExportDeclaration(node) &&
node.declaration &&
node.declaration.decorators &&
node.declaration.decorators.length > 0 &&
isNonEmptyArray(node.declaration.decorators) &&
// Only print decorators here if they were written before the export,
// otherwise they are printed by the node.declaration
locStart(node, { ignoreDecorators: true }) >
+2 -1
View File
@@ -6,6 +6,7 @@ const {
hasNewline,
hasNewlineInRange,
skipWhitespace,
isNonEmptyArray,
} = require("../common/util");
const { locStart, locEnd, hasSameLocStart } = require("./loc");
@@ -1470,7 +1471,7 @@ const getCommentTestFunction = (flags, fn) => {
* @returns {boolean}
*/
function hasComment(node, flags, fn) {
if (!node || !Array.isArray(node.comments) || node.comments.length === 0) {
if (!node || !isNonEmptyArray(node.comments)) {
return false;
}
const test = getCommentTestFunction(flags, fn);
+2 -1
View File
@@ -5,6 +5,7 @@ const {
getMinNotPresentContinuousCount,
getMaxContinuousCount,
getStringWidth,
isNonEmptyArray,
} = require("../common/util");
const {
builders: {
@@ -773,7 +774,7 @@ function printChildren(path, options, print, events) {
function getLastDescendantNode(node) {
let current = node;
while (current.children && current.children.length !== 0) {
while (isNonEmptyArray(current.children)) {
current = current.children[current.children.length - 1];
}
return current;
+6 -6
View File
@@ -1,6 +1,6 @@
"use strict";
const { getLast } = require("../common/util");
const { getLast, isNonEmptyArray } = require("../common/util");
function getAncestorCount(path, filter) {
let counter = 0;
@@ -98,7 +98,7 @@ function isLastDescendantNode(path) {
}
function getLastDescendantNode(node) {
return "children" in node && node.children.length !== 0
return isNonEmptyArray(node.children)
? getLastDescendantNode(getLast(node.children))
: node;
}
@@ -124,7 +124,7 @@ function hasPrettierIgnore(path) {
}
function isEmptyNode(node) {
return (!node.children || node.children.length === 0) && !hasComments(node);
return !isNonEmptyArray(node.children) && !hasComments(node);
}
function hasComments(node) {
@@ -138,11 +138,11 @@ function hasComments(node) {
}
function hasLeadingComments(node) {
return node && node.leadingComments && node.leadingComments.length !== 0;
return node && isNonEmptyArray(node.leadingComments);
}
function hasMiddleComments(node) {
return node && node.middleComments && node.middleComments.length !== 0;
return node && isNonEmptyArray(node.middleComments);
}
function hasIndicatorComment(node) {
@@ -154,7 +154,7 @@ function hasTrailingComment(node) {
}
function hasEndComments(node) {
return node && node.endComments && node.endComments.length !== 0;
return node && isNonEmptyArray(node.endComments);
}
/**
+2 -1
View File
@@ -24,6 +24,7 @@ const {
addLeadingComment,
addDanglingComment,
addTrailingComment,
isNonEmptyArray,
} = require("../common/util");
const childNodesCache = new WeakMap();
@@ -559,7 +560,7 @@ function printComments(path, print, options, needsSemi) {
const printed = print(path);
const comments = value && value.comments;
if (!comments || comments.length === 0) {
if (!isNonEmptyArray(comments)) {
return prependCursorPlaceholder(path, options, printed);
}