| |
| |
| |
| |
| |
| "use strict"; |
|
|
| |
| |
| |
|
|
| const astUtils = require("./utils/ast-utils"); |
|
|
| |
| |
| |
|
|
| |
| module.exports = { |
| meta: { |
| type: "suggestion", |
|
|
| defaultOptions: [ |
| { |
| allow: [], |
| ignoreDestructuring: false, |
| ignoreGlobals: false, |
| ignoreImports: false, |
| properties: "always", |
| }, |
| ], |
|
|
| docs: { |
| description: "Enforce camelcase naming convention", |
| recommended: false, |
| frozen: true, |
| url: "https://eslint.org/docs/latest/rules/camelcase", |
| }, |
|
|
| schema: [ |
| { |
| type: "object", |
| properties: { |
| ignoreDestructuring: { |
| type: "boolean", |
| }, |
| ignoreImports: { |
| type: "boolean", |
| }, |
| ignoreGlobals: { |
| type: "boolean", |
| }, |
| properties: { |
| enum: ["always", "never"], |
| }, |
| allow: { |
| type: "array", |
| items: { |
| type: "string", |
| }, |
| minItems: 0, |
| uniqueItems: true, |
| }, |
| }, |
| additionalProperties: false, |
| }, |
| ], |
|
|
| messages: { |
| notCamelCase: "Identifier '{{name}}' is not in camel case.", |
| notCamelCasePrivate: "#{{name}} is not in camel case.", |
| }, |
| }, |
|
|
| create(context) { |
| const [ |
| { |
| allow, |
| ignoreDestructuring, |
| ignoreGlobals, |
| ignoreImports, |
| properties, |
| }, |
| ] = context.options; |
| const sourceCode = context.sourceCode; |
|
|
| |
| |
| |
|
|
| |
| const reported = new Set(); |
|
|
| |
| |
| |
| |
| |
| |
| function isUnderscored(name) { |
| const nameBody = name.replace(/^_+|_+$/gu, ""); |
|
|
| |
| return ( |
| nameBody.includes("_") && nameBody !== nameBody.toUpperCase() |
| ); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function isAllowed(name) { |
| return allow.some( |
| entry => name === entry || name.match(new RegExp(entry, "u")), |
| ); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function isGoodName(name) { |
| return !isUnderscored(name) || isAllowed(name); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function isAssignmentTarget(node) { |
| const parent = node.parent; |
|
|
| switch (parent.type) { |
| case "AssignmentExpression": |
| case "AssignmentPattern": |
| return parent.left === node; |
|
|
| case "Property": |
| return ( |
| parent.parent.type === "ObjectPattern" && |
| parent.value === node |
| ); |
| case "ArrayPattern": |
| case "RestElement": |
| return true; |
|
|
| default: |
| return false; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| function equalsToOriginalName(node) { |
| const localName = node.name; |
| const valueNode = |
| node.parent.type === "AssignmentPattern" ? node.parent : node; |
| const parent = valueNode.parent; |
|
|
| switch (parent.type) { |
| case "Property": |
| return ( |
| (parent.parent.type === "ObjectPattern" || |
| parent.parent.type === "ObjectExpression") && |
| parent.value === valueNode && |
| !parent.computed && |
| parent.key.type === "Identifier" && |
| parent.key.name === localName |
| ); |
|
|
| case "ImportSpecifier": |
| return ( |
| parent.local === node && |
| astUtils.getModuleExportName(parent.imported) === |
| localName |
| ); |
|
|
| default: |
| return false; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| function report(node) { |
| if (reported.has(node.range[0])) { |
| return; |
| } |
| reported.add(node.range[0]); |
|
|
| |
| context.report({ |
| node, |
| messageId: |
| node.type === "PrivateIdentifier" |
| ? "notCamelCasePrivate" |
| : "notCamelCase", |
| data: { name: node.name }, |
| }); |
| } |
|
|
| |
| |
| |
| |
| |
| function reportReferenceId(node) { |
| |
| |
| |
| |
| if ( |
| node.parent.type === "CallExpression" || |
| node.parent.type === "NewExpression" |
| ) { |
| return; |
| } |
|
|
| |
| |
| |
| |
| |
| if ( |
| node.parent.type === "AssignmentPattern" && |
| node.parent.right === node |
| ) { |
| return; |
| } |
|
|
| |
| |
| |
| |
| if (ignoreDestructuring && equalsToOriginalName(node)) { |
| return; |
| } |
|
|
| |
| |
| |
| if (astUtils.isImportAttributeKey(node)) { |
| return; |
| } |
|
|
| report(node); |
| } |
|
|
| return { |
| |
| Program(node) { |
| const scope = sourceCode.getScope(node); |
|
|
| if (!ignoreGlobals) { |
| |
| for (const variable of scope.variables) { |
| if ( |
| variable.identifiers.length > 0 || |
| isGoodName(variable.name) |
| ) { |
| continue; |
| } |
| for (const reference of variable.references) { |
| |
| |
| |
| |
| reportReferenceId(reference.identifier); |
| } |
| } |
| } |
|
|
| |
| for (const reference of scope.through) { |
| const id = reference.identifier; |
|
|
| if ( |
| isGoodName(id.name) || |
| astUtils.isImportAttributeKey(id) |
| ) { |
| continue; |
| } |
|
|
| |
| |
| |
| |
| reportReferenceId(id); |
| } |
| }, |
|
|
| |
| [[ |
| "VariableDeclaration", |
| "FunctionDeclaration", |
| "FunctionExpression", |
| "ArrowFunctionExpression", |
| "ClassDeclaration", |
| "ClassExpression", |
| "CatchClause", |
| ]](node) { |
| for (const variable of sourceCode.getDeclaredVariables(node)) { |
| if (isGoodName(variable.name)) { |
| continue; |
| } |
| const id = variable.identifiers[0]; |
|
|
| |
| if (!(ignoreDestructuring && equalsToOriginalName(id))) { |
| report(id); |
| } |
|
|
| |
| |
| |
| |
| for (const reference of variable.references) { |
| if (reference.init) { |
| continue; |
| } |
| reportReferenceId(reference.identifier); |
| } |
| } |
| }, |
|
|
| |
| [[ |
| "ObjectExpression > Property[computed!=true] > Identifier.key", |
| "MethodDefinition[computed!=true] > Identifier.key", |
| "PropertyDefinition[computed!=true] > Identifier.key", |
| "MethodDefinition > PrivateIdentifier.key", |
| "PropertyDefinition > PrivateIdentifier.key", |
| ]](node) { |
| if ( |
| properties === "never" || |
| astUtils.isImportAttributeKey(node) || |
| isGoodName(node.name) |
| ) { |
| return; |
| } |
| report(node); |
| }, |
| "MemberExpression[computed!=true] > Identifier.property"(node) { |
| if ( |
| properties === "never" || |
| !isAssignmentTarget(node.parent) || |
| isGoodName(node.name) |
| ) { |
| return; |
| } |
| report(node); |
| }, |
|
|
| |
| ImportDeclaration(node) { |
| for (const variable of sourceCode.getDeclaredVariables(node)) { |
| if (isGoodName(variable.name)) { |
| continue; |
| } |
| const id = variable.identifiers[0]; |
|
|
| |
| if (!(ignoreImports && equalsToOriginalName(id))) { |
| report(id); |
| } |
|
|
| |
| |
| |
| |
| for (const reference of variable.references) { |
| reportReferenceId(reference.identifier); |
| } |
| } |
| }, |
|
|
| |
| [[ |
| "ExportAllDeclaration > Identifier.exported", |
| "ExportSpecifier > Identifier.exported", |
| ]](node) { |
| if (isGoodName(node.name)) { |
| return; |
| } |
| report(node); |
| }, |
|
|
| |
| [[ |
| "LabeledStatement > Identifier.label", |
|
|
| |
| |
| |
| |
| "BreakStatement > Identifier.label", |
| "ContinueStatement > Identifier.label", |
| ]](node) { |
| if (isGoodName(node.name)) { |
| return; |
| } |
| report(node); |
| }, |
| }; |
| }, |
| }; |
|
|