UNPKG

eslint-plugin-ngxs-style-guide

Version:
397 lines (385 loc) 11.1 kB
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __markAsModule = (target) => __defProp(target, "__esModule", { value: true }); var __export = (target, all) => { __markAsModule(target); for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __reExport = (target, module2, desc) => { if (module2 && typeof module2 === "object" || typeof module2 === "function") { for (let key of __getOwnPropNames(module2)) if (!__hasOwnProp.call(target, key) && key !== "default") __defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable }); } return target; }; var __toModule = (module2) => { return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2); }; // src/index.ts __export(exports, { configs: () => configs, rules: () => rules }); // src/action-suffixes/action-suffixes.ts function actionSuffixes(context) { let className; let isActionClass = false; return { ClassDeclaration(node) { className = node.id && node.id.name; }, "ClassDeclaration:exit"(node) { if (isActionClass && className && className.endsWith("Action") && node.id) { context.report({ messageId: "default", node: node.id }); } isActionClass = false; className = void 0; }, PropertyDefinition(node) { if (className && node.value && node.value.type === "Literal") { const value = String(node.value.value); if (/^\[[A-Z][A-Za-z]+/.test(value)) { isActionClass = true; } } } }; } var rule = { create: actionSuffixes, meta: { type: "suggestion", schema: {}, docs: { description: "Actions should NOT have a suffix", recommended: "warn", url: "https://www.ngxs.io/recipes/style-guide#action-suffixes" }, messages: { default: "Actions should NOT have a suffix `Action`" } } }; // src/no-pipe-dispatch/no-pipe-dispatch.ts function noPipeDispatch(context) { return { MemberExpression(node) { if (node.object.type === "CallExpression" && node.object.callee.type === "MemberExpression" && node.object.callee.property.type === "Identifier" && node.object.callee.property.name === "dispatch" && node.property.type === "Identifier" && node.property.name === "pipe") { context.report({ messageId: "default", node: node.property }); } } }; } var rule2 = { create: noPipeDispatch, meta: { type: "problem", schema: {}, docs: { description: "No pipe() after dispatch", recommended: "warn", url: "" }, messages: { default: "No pipe() after dispatch" } } }; // src/utils.ts var import_experimental_utils = __toModule(require("@typescript-eslint/experimental-utils")); function getDecoratorByName(node, name) { return node.decorators?.find((d) => { const expression = d.expression.type === "CallExpression" && d.expression; return expression && expression.callee.type === "Identifier" && expression.callee.name === name; }); } function isClassDeclaration(node) { return node.type === "ClassDeclaration"; } function hasStateDecorator(node) { const decorator = getDecoratorByName(node, "State"); return decorator != void 0; } function hasActionDecorator(node) { const decorator = getDecoratorByName(node, "Action"); return decorator != void 0; } function hasSelectDecorator(node) { const decorator = getDecoratorByName(node, "Select"); return decorator != void 0; } function isImplements(node, interfaceName) { return Boolean(node.implements?.some((node2) => { return node2.expression.type === "Identifier" && node2.expression.name === interfaceName; })); } function isIdentifierEndsWith(node, name) { if (!node.id) { return true; } return node.id.name.endsWith(name); } function getParent(node, predicate) { let result; while (node && node.parent) { node = node.parent; if (predicate(node)) { result = node; break; } } return result; } function getParentFunction(node) { return getParent(node, (n) => n.type === "MethodDefinition"); } function getParentClass(node) { return getParent(node, (n) => n.type === "ClassDeclaration"); } // src/no-subscribe-in-actions/no-subscribe-in-actions.ts function noSubscribeInActions(context) { return { MemberExpression(node) { if (node.property.type === "Identifier" && node.property.name === "subscribe") { const method = getParentFunction(node); if (method && hasActionDecorator(method)) { const pclass = getParentClass(method); if (pclass && hasStateDecorator(pclass)) { context.report({ messageId: "default", node: node.property }); } } } } }; } var rule3 = { create: noSubscribeInActions, meta: { type: "suggestion", schema: {}, docs: { description: "", recommended: "warn", url: "https://stackoverflow.com/questions/53047853" }, messages: { default: "Do not subscribe in actions, return Observable" } } }; // src/plugin-suffix/plugin-suffix.ts function pluginSuffix(context) { return { ClassDeclaration(node) { if (node.id && isImplements(node, "NgxsPlugin") && !isIdentifierEndsWith(node, "Plugin")) { context.report({ messageId: "default", node: node.id }); } } }; } var rule4 = { create: pluginSuffix, meta: { type: "suggestion", schema: {}, docs: { description: "Plugins should end with the `Plugin` suffix", recommended: "warn", url: "https://www.ngxs.io/recipes/style-guide#plugin-suffix" }, messages: { default: "Plugins should end with the `Plugin` suffix" } } }; // src/select-suffix/select-suffix.ts function selectSuffix(context) { return { PropertyDefinition(node) { if (!hasSelectDecorator(node)) { return; } const propertyName = node.key.name; if (propertyName && !propertyName.endsWith("$")) { context.report({ messageId: "default", node: node.key, fix: (fixer) => { const newName = `${propertyName}$`; return fixer.replaceTextRange(node.key.range, newName); } }); } } }; } var rule5 = { create: selectSuffix, meta: { docs: { description: "Selects should have a `$` suffix", url: "https://www.ngxs.io/recipes/style-guide#state-suffix", recommended: "warn" }, type: "suggestion", fixable: "code", messages: { default: "Selects should have a `$` suffix" }, schema: {} } }; // src/state-filenames/state-filenames.ts function stateFilenames(context) { const filenameWithExtension = context.getFilename(); if (filenameWithExtension === "<input>" || filenameWithExtension === "<text>" || !filenameWithExtension) { return {}; } let hasDecorator = false; return { ClassDeclaration(node) { hasDecorator = hasStateDecorator(node); }, Program() { hasDecorator = false; }, "Program:exit"(node) { if (hasDecorator && !filenameWithExtension.endsWith(".state.ts")) { context.report({ messageId: "default", node }); } } }; } var rule6 = { create: stateFilenames, meta: { docs: { description: "States should have a `.state.ts` suffix for the filename", url: "https://www.ngxs.io/recipes/style-guide#state-filenames", recommended: "warn" }, type: "suggestion", messages: { default: "States should have a `.state.ts` suffix for the filename" }, schema: {} } }; // src/state-interfaces/state-interfaces.ts function stateInterfaces(context) { return { ClassDeclaration(node) { if (!isClassDeclaration(node)) { throw new TypeError(`Unexpected node type (${node.type}), expected ClassDeclaration`); } const decoratorNode = getDecoratorByName(node, "State"); const typeName = decoratorNode?.expression?.typeParameters?.params?.[0]?.typeName; if (typeName && !typeName.name.endsWith("Model")) { context.report({ messageId: "default", node, fix: (fixer) => { let result = null; if (node.id) { const newName = `${typeName.name}Model`; result = fixer.replaceTextRange(typeName.range, newName); } return result; } }); } } }; } var rule7 = { create: stateInterfaces, meta: { type: "suggestion", fixable: "code", schema: {}, docs: { description: "State interfaces should be named the name of the state followed by the `Model` suffix", recommended: "warn", url: "https://www.ngxs.io/recipes/style-guide#state-interfaces" }, messages: { default: "State interfaces should be named the name of the state followed by the `Model` suffix" } } }; // src/state-suffix/state-suffix.ts function stateSuffix(context) { return { ClassDeclaration(node) { if (hasStateDecorator(node) && node.id && !isIdentifierEndsWith(node, "State")) { context.report({ messageId: "default", node: node.id, fix: (fixer) => { let result = null; if (node.id) { const newName = `${node.id.name}State`; result = fixer.replaceTextRange(node.id.range, newName); } return result; } }); } } }; } var rule8 = { create: stateSuffix, meta: { type: "suggestion", fixable: "code", schema: {}, docs: { description: "A state should always be suffixed with the word `State`", recommended: "warn", url: "https://www.ngxs.io/recipes/style-guide#state-suffix" }, messages: { default: "A state should always be suffixed with the word `State`" } } }; // src/index.ts var rules = { "state-filenames": rule6, "state-interfaces": rule7, "action-suffixes": rule, "state-suffix": rule8, "plugin-suffix": rule4, "select-suffix": rule5, "no-subscribe-in-actions": rule3, "no-pipe-dispatch": rule2 }; var configs = { recommended: { rules: Object.fromEntries(Object.keys(rules).filter((rule9) => rule9 !== "no-pipe-dispatch").map((rule9) => [`ngxs-style-guide/${rule9}`, "warn"])) } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { configs, rules }); //# sourceMappingURL=index.js.map