@vanta-inc/eslint-plugin-vanta
Version:
Custom ESLint rules for Vanta
146 lines (145 loc) • 7.16 kB
JavaScript
"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;