UNPKG

eslint-plugin-complete

Version:

An ESLint plugin that contains useful rules.

113 lines (112 loc) 4.31 kB
import { AST_NODE_TYPES } from "@typescript-eslint/utils"; import { isFirstLetterCapitalized } from "../completeCommon.js"; import { createRule } from "../utils.js"; const ARRAY_OR_OBJECT_EXPRESSION_TYPES = new Set([ AST_NODE_TYPES.ObjectExpression, AST_NODE_TYPES.ArrayExpression, AST_NODE_TYPES.TSSatisfiesExpression, ]); const AUTO_FIX_TYPE_BLACKLIST = new Set([ // If the variable is already being casted to something with "as", then don't apply any fix to // avoid stomping on the existing code. AST_NODE_TYPES.TSAsExpression, // The `satisfies` keyword will mess up the auto-fixer, because "as const" will be placed after // the satisfies part. AST_NODE_TYPES.TSSatisfiesExpression, ]); export const requireCapitalConstAssertions = createRule({ name: "require-capital-const-assertions", meta: { type: "problem", docs: { description: "Requires a capital letter for named objects and arrays that have a const assertion", recommended: true, requiresTypeChecking: false, }, schema: [], messages: { noConstAssertion: "Variables with capital letters that assign an object or array must use const assertions.", }, fixable: "code", }, defaultOptions: [], create(context) { return { VariableDeclaration(node) { if (node.kind !== "const") { return; } for (const declaration of node.declarations) { const { id } = declaration; if (id.type !== AST_NODE_TYPES.Identifier) { continue; } if (!isFirstLetterCapitalized(id.name)) { continue; } const { init } = declaration; if (init === null) { continue; } // Do nothing if this is not an object or array expression. if (!ARRAY_OR_OBJECT_EXPRESSION_TYPES.has(init.type)) { continue; } if (hasConstAssertion(init)) { continue; } context.report({ loc: node.loc, messageId: "noConstAssertion", fix: (fixer) => { // If this variable isn't being assigned to anything, then there is nothing we can // fix. if (declaration.init === null) { return null; } if (declaration.init.type === AST_NODE_TYPES.TSAsExpression) { return null; } if (AUTO_FIX_TYPE_BLACKLIST.has(declaration.init.type)) { return null; } return fixer.insertTextAfter(declaration.init, " as const"); }, }); } }, }; }, }); function hasConstAssertion(init) { switch (init.type) { case AST_NODE_TYPES.TSAsExpression: { return hasConstAssertionWithoutSatisfies(init); } case AST_NODE_TYPES.TSSatisfiesExpression: { return hasConstAssertionWithSatisfies(init); } default: { return false; } } } function hasConstAssertionWithoutSatisfies(init) { const { typeAnnotation } = init; if (typeAnnotation.type !== AST_NODE_TYPES.TSTypeReference) { return false; } const { typeName } = typeAnnotation; if (typeName.type !== AST_NODE_TYPES.Identifier) { return false; } return typeName.name === "const"; } /** The "as const" part is nested within the `TSSatisfiesExpression` node as another expression. */ function hasConstAssertionWithSatisfies(init) { const { expression } = init; if (expression.type !== AST_NODE_TYPES.TSAsExpression) { return false; } return hasConstAssertionWithoutSatisfies(expression); }