UNPKG

@vanta-inc/eslint-plugin-vanta

Version:
146 lines (145 loc) 7.16 kB
"use strict"; /** * @fileoverview Follow naming conventions for Mongoose models and schemas. * For a collection X, we use the following naming conventions: * * XFields = typeof schema; * XDoc = XFields & mongoose.Document; * XModel = model<XDoc>(schema, options); */ Object.defineProperty(exports, "__esModule", { value: true }); var utils_1 = require("@typescript-eslint/utils"); var MONGOOSE_DOCUMENT_TYPE_NODE_IDENTIFIERS = new Set([ "mongoose.Document", "Document", ]); var MONGOOSE_MODEL_TYPE_NODE_IDENTIFIERS = new Set([ "mongoose.model", "model", "mongoose.Model", "Model", ]); var EXPECTED_TYPE_ALIAS_SUFFIX = "Doc"; var EXPECTED_INTERSECTION_TYPE_NAME_SUFFIX = "Fields"; var EXPECTED_MODEL_VARIABLE_SUFFIX = "Model"; var rule = utils_1.ESLintUtils.RuleCreator(function (ruleName) { return "https://github.com/VantaInc/eslint-plugin-vanta/blob/main/docs/rules/" + ruleName + ".md"; })({ name: "mongoose-naming-convention", meta: { docs: { description: "Follow naming conventions for Mongoose modules and schemas.", recommended: "error", }, messages: { mongooseDocumentNaming: "Type alias names for mongoose documents must be suffixed with 'Doc', got {{typeAliasName}}", mongooseFieldsNaming: "Types intersected with mongoose.Document must be suffixed with 'Fields', got {{typeName}}", mongooseDocumentFieldsMismatch: "Prefixes of type alias name for mongoose documents must match the prefixes of types unioned, got {{typeAliasNamePrefix}} and {{fieldTypeNamePrefix}}", mongooseModelNaming: "Mongoose models must be suffixed with 'Model', got {{variableName}}", mongooseModelDocumentTypeNaming: "Mongoose model initialized with type that is not suffixed by 'Doc', got {{documentTypeName}}", mongooseModelDocumentTypeMismatch: "Prefixes of variable declaration for mongoose models must match the prefix of document type, got {{variableDeclarationNamePrefix}} and {{documentTypeNamePrefix}}", }, type: "suggestion", schema: [], }, defaultOptions: [], create: function (context) { var sourceCode = context.getSourceCode(); return { "TSTypeAliasDeclaration > TSIntersectionType > TSTypeReference": function (node) { if (!MONGOOSE_DOCUMENT_TYPE_NODE_IDENTIFIERS.has(sourceCode.getText(node))) { return; } var ancestors = context.getAncestors(); var intersection = ancestors.slice(-1)[0]; var typeAliasDeclaration = ancestors.slice(-2)[0]; var typeAliasName = typeAliasDeclaration.id.name; if (!typeAliasName.endsWith(EXPECTED_TYPE_ALIAS_SUFFIX)) { context.report({ node: typeAliasDeclaration.id, messageId: "mongooseDocumentNaming", data: { typeAliasName: typeAliasDeclaration.id.name, }, }); return; } var typeAliasNamePrefix = typeAliasName.slice(0, -EXPECTED_TYPE_ALIAS_SUFFIX.length); var siblingTypes = intersection.types.filter(function (type) { return !MONGOOSE_DOCUMENT_TYPE_NODE_IDENTIFIERS.has(sourceCode.getText(type)) && type.type === utils_1.AST_NODE_TYPES.TSTypeReference; }); siblingTypes.forEach(function (sibling) { var siblingTypeName = sourceCode.getText(sibling.typeName); if (!siblingTypeName.endsWith(EXPECTED_INTERSECTION_TYPE_NAME_SUFFIX)) { context.report({ node: sibling, messageId: "mongooseFieldsNaming", data: { typeName: sourceCode.getText(sibling), }, }); return; } var siblingTypeNamePrefix = siblingTypeName.slice(0, -EXPECTED_INTERSECTION_TYPE_NAME_SUFFIX.length); if (typeAliasNamePrefix !== siblingTypeNamePrefix) { context.report({ node: sibling, messageId: "mongooseDocumentFieldsMismatch", data: { typeAliasNamePrefix: typeAliasNamePrefix, fieldTypeNamePrefix: siblingTypeNamePrefix, }, }); return; } }); return; }, "VariableDeclarator > CallExpression > TSTypeParameterInstantiation > TSTypeReference": function (node) { var ancestors = context.getAncestors(); var callExpressionNode = ancestors.slice(-2)[0]; var variableDeclaratorNode = ancestors.slice(-3)[0]; if (!MONGOOSE_MODEL_TYPE_NODE_IDENTIFIERS.has(sourceCode.getText(callExpressionNode.callee))) { return; } var variableDeclarationName = sourceCode.getText(variableDeclaratorNode.id); if (!variableDeclarationName.endsWith(EXPECTED_MODEL_VARIABLE_SUFFIX)) { context.report({ node: variableDeclaratorNode.id, messageId: "mongooseModelNaming", data: { variableName: sourceCode.getText(variableDeclaratorNode.id), }, }); return; } var documentTypeName = sourceCode.getText(node); if (!documentTypeName.endsWith(EXPECTED_TYPE_ALIAS_SUFFIX)) { context.report({ node: node, messageId: "mongooseModelDocumentTypeNaming", data: { documentTypeName: documentTypeName, }, }); return; } var variableDeclarationNamePrefix = variableDeclarationName.slice(0, -EXPECTED_MODEL_VARIABLE_SUFFIX.length); var documentTypeNamePrefix = documentTypeName.slice(0, -EXPECTED_TYPE_ALIAS_SUFFIX.length); if (variableDeclarationNamePrefix !== documentTypeNamePrefix) { context.report({ node: node, messageId: "mongooseModelDocumentTypeMismatch", data: { variableDeclarationNamePrefix: variableDeclarationNamePrefix, documentTypeNamePrefix: documentTypeNamePrefix, }, }); return; } }, }; }, }); module.exports = rule; exports.default = rule;