From 89402482ee6b1e966eb4e0ff41a45af9c5382843 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 15 Dec 2020 12:23:40 +0800 Subject: [PATCH] Consistent `Jsx` case in variables (#9871) --- .eslintrc.yml | 3 +- .../index.js | 1 + .../jsx-identifier-case.js | 46 +++++++++++++++++++ src/language-js/comments.js | 4 +- src/language-js/print/binaryish.js | 10 ++-- src/language-js/print/call-arguments.js | 4 +- src/language-js/print/function.js | 4 +- src/language-js/print/jsx.js | 42 ++++++++--------- src/language-js/print/statement.js | 8 ++-- src/language-js/print/ternary.js | 14 +++--- src/language-js/printer-estree.js | 4 +- src/language-js/utils.js | 30 ++++++------ 12 files changed, 109 insertions(+), 61 deletions(-) create mode 100644 scripts/tools/eslint-plugin-prettier-internal-rules/jsx-identifier-case.js diff --git a/.eslintrc.yml b/.eslintrc.yml index 4b4ebfc6e..07ed97f37 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -13,7 +13,6 @@ plugins: settings: import/internal-regex: ^linguist-languages/ rules: - prettier-internal-rules/require-json-extensions: error curly: error dot-notation: error eqeqeq: @@ -52,6 +51,8 @@ rules: prefer-object-spread: error prefer-rest-params: error prefer-spread: error + prettier-internal-rules/jsx-identifier-case: error + prettier-internal-rules/require-json-extensions: error quotes: - error - double diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/index.js b/scripts/tools/eslint-plugin-prettier-internal-rules/index.js index 412f6a982..3dffdeef9 100644 --- a/scripts/tools/eslint-plugin-prettier-internal-rules/index.js +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/index.js @@ -4,6 +4,7 @@ module.exports = { rules: { "better-parent-property-check-in-needs-parens": require("./better-parent-property-check-in-needs-parens"), "directly-loc-start-end": require("./directly-loc-start-end"), + "jsx-identifier-case": require("./jsx-identifier-case"), "no-node-comments": require("./no-node-comments"), "prefer-fast-path-each": require("./prefer-fast-path-each"), "require-json-extensions": require("./require-json-extensions"), diff --git a/scripts/tools/eslint-plugin-prettier-internal-rules/jsx-identifier-case.js b/scripts/tools/eslint-plugin-prettier-internal-rules/jsx-identifier-case.js new file mode 100644 index 000000000..c1063a416 --- /dev/null +++ b/scripts/tools/eslint-plugin-prettier-internal-rules/jsx-identifier-case.js @@ -0,0 +1,46 @@ +"use strict"; + +const MESSAGE_ID = "jsx-identifier-case"; + +// To ignore variables, config eslint like this +// {'prettier-internal-rules/jsx-identifier-case': ['error', 'name1', ... 'nameN']} + +module.exports = { + meta: { + type: "suggestion", + docs: { + url: + "https://github.com/prettier/prettier/blob/master/scripts/eslint-plugin-prettier-internal-rules/jsx-identifier-case.js", + }, + messages: { + [MESSAGE_ID]: "Please rename '{{name}}' to '{{fixed}}'.", + }, + fixable: "code", + }, + create(context) { + const ignored = new Set(context.options); + return { + "Identifier[name=/JSX/]:not(ObjectExpression > Property.properties > .key)"( + node + ) { + const { name } = node; + + if (ignored.has(name)) { + return; + } + + const fixed = name.replace(/JSX/g, "Jsx"); + context.report({ + node, + messageId: MESSAGE_ID, + data: { name, fixed }, + fix: (fixer) => fixer.replaceText(node, fixed), + }); + }, + }; + }, + schema: { + type: "array", + uniqueItems: true, + }, +}; diff --git a/src/language-js/comments.js b/src/language-js/comments.js index 98b1e760a..128026d41 100644 --- a/src/language-js/comments.js +++ b/src/language-js/comments.js @@ -15,7 +15,7 @@ const { isBlockComment, getFunctionParameters, isPrettierIgnoreComment, - isJSXNode, + isJsxNode, hasFlowShorthandAnnotationComment, hasFlowAnnotationComment, hasIgnoreComment, @@ -956,7 +956,7 @@ function willPrintOwnComments(path /*, options */) { return ( ((node && - (isJSXNode(node) || + (isJsxNode(node) || hasFlowShorthandAnnotationComment(node) || (parent && (parent.type === "CallExpression" || diff --git a/src/language-js/print/binaryish.js b/src/language-js/print/binaryish.js index a1c736d88..4caa4725f 100644 --- a/src/language-js/print/binaryish.js +++ b/src/language-js/print/binaryish.js @@ -9,7 +9,7 @@ const { const { hasLeadingOwnLineComment, isBinaryish, - isJSXNode, + isJsxNode, shouldFlatten, hasComment, CommentCheckFlags, @@ -127,7 +127,7 @@ function printBinaryishExpression(path, options, print) { // // ) - const hasJSX = isJSXNode(n.right); + const hasJsx = isJsxNode(n.right); const firstGroupIndex = parts.findIndex( (part) => typeof part !== "string" && part.type === "group" @@ -139,7 +139,7 @@ function printBinaryishExpression(path, options, print) { firstGroupIndex === -1 ? 1 : firstGroupIndex + 1 ); - const rest = concat(parts.slice(headParts.length, hasJSX ? -1 : undefined)); + const rest = concat(parts.slice(headParts.length, hasJsx ? -1 : undefined)); const groupId = Symbol("logicalChain-" + ++uid); @@ -154,7 +154,7 @@ function printBinaryishExpression(path, options, print) { { id: groupId } ); - if (!hasJSX) { + if (!hasJsx) { return chain; } @@ -302,7 +302,7 @@ function shouldInlineLogicalExpression(node) { return true; } - if (isJSXNode(node.right)) { + if (isJsxNode(node.right)) { return true; } diff --git a/src/language-js/print/call-arguments.js b/src/language-js/print/call-arguments.js index d5c943eb0..24c8e9221 100644 --- a/src/language-js/print/call-arguments.js +++ b/src/language-js/print/call-arguments.js @@ -12,7 +12,7 @@ const { hasComment, CommentCheckFlags, isFunctionCompositionArgs, - isJSXNode, + isJsxNode, isLongCurriedCallExpression, shouldPrintComma, getCallArguments, @@ -263,7 +263,7 @@ function couldGroupArg(arg) { arg.body.type === "CallExpression" || arg.body.type === "OptionalCallExpression" || arg.body.type === "ConditionalExpression" || - isJSXNode(arg.body))) + isJsxNode(arg.body))) ); } diff --git a/src/language-js/print/function.js b/src/language-js/print/function.js index 5037c16ba..18c8e513c 100644 --- a/src/language-js/print/function.js +++ b/src/language-js/print/function.js @@ -13,7 +13,7 @@ const { getFunctionParameters, hasLeadingOwnLineComment, isFlowAnnotationComment, - isJSXNode, + isJsxNode, isTemplateOnItsOwnLine, shouldPrintComma, startsWithNoLookaheadToken, @@ -190,7 +190,7 @@ function printArrowFunctionExpression(path, options, print, args) { (n.body.type === "ArrayExpression" || n.body.type === "ObjectExpression" || n.body.type === "BlockStatement" || - isJSXNode(n.body) || + isJsxNode(n.body) || isTemplateOnItsOwnLine(n.body, options.originalText) || n.body.type === "ArrowFunctionExpression" || n.body.type === "DoExpression") diff --git a/src/language-js/print/jsx.js b/src/language-js/print/jsx.js index c2a621527..09d55e22b 100644 --- a/src/language-js/print/jsx.js +++ b/src/language-js/print/jsx.js @@ -20,10 +20,10 @@ const { const { getLast, getPreferredQuote } = require("../../common/util"); const { - isEmptyJSXElement, - isJSXWhitespaceExpression, - isJSXNode, - isMeaningfulJSXText, + isEmptyJsxElement, + isJsxWhitespaceExpression, + isJsxNode, + isMeaningfulJsxText, matchJsxWhitespaceRegex, rawText, isLiteral, @@ -54,7 +54,7 @@ const { willPrintOwnComments } = require("../comments"); function printJsxElementInternal(path, options, print) { const n = path.getValue(); - if (n.type === "JSXElement" && isEmptyJSXElement(n)) { + if (n.type === "JSXElement" && isEmptyJsxElement(n)) { return concat([ path.call(print, "openingElement"), path.call(print, "closingElement"), @@ -87,7 +87,7 @@ function printJsxElementInternal(path, options, print) { // This makes it easy to turn them into `jsxWhitespace` which // can then print as either a space or `{" "}` when breaking. n.children = n.children.map((child) => { - if (isJSXWhitespaceExpression(child)) { + if (isJsxWhitespaceExpression(child)) { return { type: "JSXText", value: " ", @@ -97,7 +97,7 @@ function printJsxElementInternal(path, options, print) { return child; }); - const containsTag = n.children.filter(isJSXNode).length > 0; + const containsTag = n.children.filter(isJsxNode).length > 0; const containsMultipleExpressions = n.children.filter((child) => child.type === "JSXExpressionContainer") .length > 1; @@ -123,7 +123,7 @@ function printJsxElementInternal(path, options, print) { n.openingElement.name && n.openingElement.name.name === "fbt"; - const children = printJSXChildren( + const children = printJsxChildren( path, options, print, @@ -131,7 +131,7 @@ function printJsxElementInternal(path, options, print) { isFacebookTranslationTag ); - const containsText = n.children.some((child) => isMeaningfulJSXText(child)); + const containsText = n.children.some((child) => isMeaningfulJsxText(child)); // We can end up we multiple whitespace elements with empty string // content between them. @@ -143,15 +143,15 @@ function printJsxElementInternal(path, options, print) { children[i] === hardline && children[i + 1] === "" && children[i + 2] === hardline; - const isLineFollowedByJSXWhitespace = + const isLineFollowedByJsxWhitespace = (children[i] === softline || children[i] === hardline) && children[i + 1] === "" && children[i + 2] === jsxWhitespace; - const isJSXWhitespaceFollowedByLine = + const isJsxWhitespaceFollowedByLine = children[i] === jsxWhitespace && children[i + 1] === "" && (children[i + 2] === softline || children[i + 2] === hardline); - const isDoubleJSXWhitespace = + const isDoubleJsxWhitespace = children[i] === jsxWhitespace && children[i + 1] === "" && children[i + 2] === jsxWhitespace; @@ -166,12 +166,12 @@ function printJsxElementInternal(path, options, print) { if ( (isPairOfHardlines && containsText) || isPairOfEmptyStrings || - isLineFollowedByJSXWhitespace || - isDoubleJSXWhitespace || + isLineFollowedByJsxWhitespace || + isDoubleJsxWhitespace || isPairOfHardOrSoftLines ) { children.splice(i, 2); - } else if (isJSXWhitespaceFollowedByLine) { + } else if (isJsxWhitespaceFollowedByLine) { children.splice(i + 1, 2); } } @@ -270,7 +270,7 @@ function printJsxElementInternal(path, options, print) { // This requires that we give it an array of alternating // content and whitespace elements. // To ensure this we add dummy `""` content elements as needed. -function printJSXChildren( +function printJsxChildren( path, options, print, @@ -286,7 +286,7 @@ function printJSXChildren( const text = rawText(child); // Contains a non-whitespace character - if (isMeaningfulJSXText(child)) { + if (isMeaningfulJsxText(child)) { const words = text.split(matchJsxWhitespaceRegex); // Starts with whitespace @@ -371,7 +371,7 @@ function printJSXChildren( const next = n.children[i + 1]; const directlyFollowedByMeaningfulText = - next && isMeaningfulJSXText(next); + next && isMeaningfulJsxText(next); if (directlyFollowedByMeaningfulText) { const firstWord = rawText(next) .trim() @@ -433,7 +433,7 @@ function separatorWithWhitespace( return hardline; } -function maybeWrapJSXElementInParens(path, elem, options) { +function maybeWrapJsxElementInParens(path, elem, options) { const parent = path.getParentNode(); /* istanbul ignore next */ if (!parent) { @@ -519,7 +519,7 @@ function printJsxExpressionContainer(path, options, print) { n.expression.type === "TemplateLiteral" || n.expression.type === "TaggedTemplateExpression" || n.expression.type === "DoExpression" || - (isJSXNode(parent) && + (isJsxNode(parent) && (n.expression.type === "ConditionalExpression" || isBinaryish(n.expression))))); @@ -685,7 +685,7 @@ function printJsxElement(path, options, print) { () => printJsxElementInternal(path, options, print), options ); - return maybeWrapJSXElementInParens(path, elem, options); + return maybeWrapJsxElementInParens(path, elem, options); } function printJsxEmptyExpression(path, options /*, print*/) { diff --git a/src/language-js/print/statement.js b/src/language-js/print/statement.js index 427a65295..e95592c3f 100644 --- a/src/language-js/print/statement.js +++ b/src/language-js/print/statement.js @@ -10,9 +10,9 @@ const { classPropMayCauseASIProblems, getLeftSidePathName, hasNakedLeftSide, - isJSXNode, + isJsxNode, isLastStatement, - isTheOnlyJSXElementInMarkdown, + isTheOnlyJsxElementInMarkdown, hasComment, CommentCheckFlags, } = require("../utils"); @@ -46,7 +46,7 @@ function printStatement({ path, index, bodyNode, isClass }, options, print) { if ( !options.semi && !isClass && - !isTheOnlyJSXElementInMarkdown(options, path) && + !isTheOnlyJsxElementInMarkdown(options, path) && statementNeedsASIProtection(path, options) ) { if (hasComment(node, CommentCheckFlags.Leading)) { @@ -130,7 +130,7 @@ function expressionNeedsASIProtection(path, options) { (node.operator === "+" || node.operator === "-")) || node.type === "TemplateLiteral" || node.type === "TemplateElement" || - isJSXNode(node) || + isJsxNode(node) || (node.type === "BindExpression" && !node.object) || node.type === "RegExpLiteral" || (node.type === "Literal" && node.pattern) || diff --git a/src/language-js/print/ternary.js b/src/language-js/print/ternary.js index 43da1f587..031a9dc18 100644 --- a/src/language-js/print/ternary.js +++ b/src/language-js/print/ternary.js @@ -2,7 +2,7 @@ const flat = require("lodash/flatten"); const { hasNewlineInRange } = require("../../common/util"); -const { isJSXNode, isBlockComment, getComments } = require("../utils"); +const { isJsxNode, isBlockComment, getComments } = require("../utils"); const { locStart, locEnd } = require("../loc"); const { builders: { @@ -116,8 +116,8 @@ function getConditionalChainContents(node) { return nonConditionalExpressions; } -function conditionalExpressionChainContainsJSX(node) { - return getConditionalChainContents(node).some(isJSXNode); +function conditionalExpressionChainContainsJsx(node) { + return getConditionalChainContents(node).some(isJsxNode); } function printTernaryTest(path, options, print) { @@ -210,10 +210,10 @@ function printTernary(path, options, print) { if ( isConditionalExpression && - (isJSXNode(node[testNodePropertyNames[0]]) || - isJSXNode(consequentNode) || - isJSXNode(alternateNode) || - conditionalExpressionChainContainsJSX(lastConditionalParent)) + (isJsxNode(node[testNodePropertyNames[0]]) || + isJsxNode(consequentNode) || + isJsxNode(alternateNode) || + conditionalExpressionChainContainsJsx(lastConditionalParent)) ) { jsxMode = true; forceNoIndent = true; diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index 7dff1ad22..7ca7d9412 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -43,7 +43,7 @@ const { isExportDeclaration, isFunctionNotation, isGetterOrSetter, - isTheOnlyJSXElementInMarkdown, + isTheOnlyJsxElementInMarkdown, isBlockComment, isLineComment, needsHardlineAfterDanglingComment, @@ -321,7 +321,7 @@ function printPathNoParens(path, options, print, args) { // Do not append semicolon after the only JSX element in a program return concat([ path.call(print, "expression"), - isTheOnlyJSXElementInMarkdown(options, path) ? "" : semi, + isTheOnlyJsxElementInMarkdown(options, path) ? "" : semi, ]); // Babel non-standard node. Used for Closure-style type casts. See postprocess.js. case "ParenthesizedExpression": { diff --git a/src/language-js/utils.js b/src/language-js/utils.js index fb3a2acc9..91ca8a82c 100644 --- a/src/language-js/utils.js +++ b/src/language-js/utils.js @@ -318,18 +318,18 @@ function isAngularTestWrapper(node) { * @param {Node} node * @returns {boolean} */ -function isJSXNode(node) { +function isJsxNode(node) { return node.type === "JSXElement" || node.type === "JSXFragment"; } -function isTheOnlyJSXElementInMarkdown(options, path) { +function isTheOnlyJsxElementInMarkdown(options, path) { if (options.parentParser !== "markdown" && options.parentParser !== "mdx") { return false; } const node = path.getNode(); - if (!node.expression || !isJSXNode(node.expression)) { + if (!node.expression || !isJsxNode(node.expression)) { return false; } @@ -339,7 +339,7 @@ function isTheOnlyJSXElementInMarkdown(options, path) { } // Detect an expression node representing `{" "}` -function isJSXWhitespaceExpression(node) { +function isJsxWhitespaceExpression(node) { return ( node.type === "JSXExpressionContainer" && isLiteral(node.expression) && @@ -744,7 +744,7 @@ const containsNonJsxWhitespaceRegex = new RegExp( * @param {Node} node * @returns {boolean} */ -function isMeaningfulJSXText(node) { +function isMeaningfulJsxText(node) { return ( isLiteral(node) && (containsNonJsxWhitespaceRegex.test(rawText(node)) || @@ -759,7 +759,7 @@ function isMeaningfulJSXText(node) { function hasJsxIgnoreComment(path) { const node = path.getValue(); const parent = path.getParentNode(); - if (!parent || !node || !isJSXNode(node) || !isJSXNode(parent)) { + if (!parent || !node || !isJsxNode(node) || !isJsxNode(parent)) { return false; } @@ -768,7 +768,7 @@ function hasJsxIgnoreComment(path) { let prevSibling = null; for (let i = index; i > 0; i--) { const candidate = parent.children[i - 1]; - if (candidate.type === "JSXText" && !isMeaningfulJSXText(candidate)) { + if (candidate.type === "JSXText" && !isMeaningfulJsxText(candidate)) { continue; } prevSibling = candidate; @@ -787,7 +787,7 @@ function hasJsxIgnoreComment(path) { * @param {JSXElement} node * @returns {boolean} */ -function isEmptyJSXElement(node) { +function isEmptyJsxElement(node) { if (node.children.length === 0) { return true; } @@ -798,7 +798,7 @@ function isEmptyJSXElement(node) { // if there is one text child and does not contain any meaningful text // we can treat the element as empty. const child = node.children[0]; - return isLiteral(child) && !isMeaningfulJSXText(child); + return isLiteral(child) && !isMeaningfulJsxText(child); } /** @@ -846,7 +846,7 @@ function isFlowAnnotationComment(text, typeAnnotation) { * @returns {boolean} */ function hasLeadingOwnLineComment(text, node) { - if (isJSXNode(node)) { + if (isJsxNode(node)) { return hasNodeIgnoreComment(node); } @@ -1529,7 +1529,7 @@ module.exports = { isLineComment, isPrettierIgnoreComment, isCallOrOptionalCallExpression, - isEmptyJSXElement, + isEmptyJsxElement, isExportDeclaration, isFlowAnnotationComment, isFunctionCompositionArgs, @@ -1537,13 +1537,13 @@ module.exports = { isFunctionOrArrowExpression, isGetterOrSetter, isJestEachTemplateLiteral, - isJSXNode, - isJSXWhitespaceExpression, + isJsxNode, + isJsxWhitespaceExpression, isLastStatement, isLiteral, isLongCurriedCallExpression, isSimpleCallArgument, - isMeaningfulJSXText, + isMeaningfulJsxText, isMemberExpressionChain, isMemberish, isNumericLiteral, @@ -1556,7 +1556,7 @@ module.exports = { isStringPropSafeToUnquote, isTemplateOnItsOwnLine, isTestCall, - isTheOnlyJSXElementInMarkdown, + isTheOnlyJsxElementInMarkdown, isTSXFile, isTypeAnnotationAFunction, matchJsxWhitespaceRegex,