eslint-plugin-modularity 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 Ofri Peretz
4
+ * Licensed under the MIT License. Use of this source code is governed by the
5
+ * MIT license that can be found in the LICENSE file.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.dddValueObjectImmutability = void 0;
9
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
+ const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
+ /**
12
+ * Check if a class name suggests a value object
13
+ */
14
+ function isValueObject(className, patterns) {
15
+ return patterns.some(pattern => className.includes(pattern));
16
+ }
17
+ /**
18
+ * Check if property has readonly modifier
19
+ */
20
+ function hasReadonlyModifier(node) {
21
+ if (node.type === 'PropertyDefinition') {
22
+ return node.readonly === true;
23
+ }
24
+ /* v8 ignore start -- TSPropertySignature is for interfaces, rule only processes classes */
25
+ if (node.type === 'TSPropertySignature') {
26
+ return node.readonly === true;
27
+ }
28
+ return false;
29
+ /* v8 ignore stop */
30
+ }
31
+ /**
32
+ * Check if class uses Object.freeze
33
+ */
34
+ function usesObjectFreeze(node) {
35
+ // Check constructor for Object.freeze(this)
36
+ for (const member of node.body.body) {
37
+ if (member.type === 'MethodDefinition' && member.kind === 'constructor') {
38
+ const constructor = member.value;
39
+ if (constructor.type === 'FunctionExpression') {
40
+ const body = constructor.body;
41
+ if (body.type === 'BlockStatement') {
42
+ for (const statement of body.body) {
43
+ if (statement.type === 'ExpressionStatement' &&
44
+ statement.expression.type === 'CallExpression' &&
45
+ statement.expression.callee.type === 'MemberExpression' &&
46
+ statement.expression.callee.object.type === 'Identifier' &&
47
+ statement.expression.callee.object.name === 'Object' &&
48
+ statement.expression.callee.property.type === 'Identifier' &&
49
+ statement.expression.callee.property.name === 'freeze') {
50
+ return true;
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+ return false;
58
+ }
59
+ exports.dddValueObjectImmutability = (0, eslint_devkit_2.createRule)({
60
+ name: 'ddd-value-object-immutability',
61
+ meta: {
62
+ type: 'suggestion',
63
+ docs: {
64
+ description: 'Validates value objects are properly immutable',
65
+ },
66
+ messages: {
67
+ mutableValueObject: (0, eslint_devkit_1.formatLLMMessage)({
68
+ icon: eslint_devkit_1.MessageIcons.ARCHITECTURE,
69
+ issueName: 'Mutable value object',
70
+ description: 'Value object {{className}} has mutable properties',
71
+ severity: 'MEDIUM',
72
+ fix: 'Make properties readonly or use Object.freeze',
73
+ documentationLink: 'https://martinfowler.com/bliki/ValueObject.html',
74
+ }),
75
+ mutableNestedType: (0, eslint_devkit_1.formatLLMMessage)({
76
+ icon: eslint_devkit_1.MessageIcons.ARCHITECTURE,
77
+ issueName: 'Mutable nested type',
78
+ description: 'Value object {{className}} has mutable nested type (array/object) - use Readonly<T> or readonly arrays',
79
+ severity: 'MEDIUM',
80
+ fix: 'Use readonly arrays or Readonly<T> for nested objects',
81
+ documentationLink: 'https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype',
82
+ }),
83
+ addReadonly: (0, eslint_devkit_1.formatLLMMessage)({
84
+ icon: eslint_devkit_1.MessageIcons.INFO,
85
+ issueName: 'Add Readonly',
86
+ description: 'Add readonly modifier',
87
+ severity: 'LOW',
88
+ fix: 'readonly propertyName: Type',
89
+ documentationLink: 'https://www.typescriptlang.org/docs/handbook/2/classes.html#readonly',
90
+ }),
91
+ useObjectFreeze: (0, eslint_devkit_1.formatLLMMessage)({
92
+ icon: eslint_devkit_1.MessageIcons.INFO,
93
+ issueName: 'Use Object.freeze',
94
+ description: 'Use Object.freeze in constructor',
95
+ severity: 'LOW',
96
+ fix: 'constructor() { Object.freeze(this); }',
97
+ documentationLink: 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze',
98
+ }),
99
+ makeImmutable: (0, eslint_devkit_1.formatLLMMessage)({
100
+ icon: eslint_devkit_1.MessageIcons.INFO,
101
+ issueName: 'Make Immutable',
102
+ description: 'Make value object fully immutable',
103
+ severity: 'LOW',
104
+ fix: 'Add readonly to all properties and freeze object',
105
+ documentationLink: 'https://martinfowler.com/bliki/ValueObject.html',
106
+ }),
107
+ },
108
+ schema: [
109
+ {
110
+ type: 'object',
111
+ properties: {
112
+ valueObjectPatterns: {
113
+ type: 'array',
114
+ items: { type: 'string' },
115
+ default: ['Value', 'VO', 'ValueObject'],
116
+ description: 'Patterns to identify value objects',
117
+ },
118
+ requireReadonly: {
119
+ type: 'boolean',
120
+ default: true,
121
+ description: 'Require readonly modifiers',
122
+ },
123
+ checkObjectFreeze: {
124
+ type: 'boolean',
125
+ default: true,
126
+ description: 'Check for Object.freeze usage',
127
+ },
128
+ },
129
+ additionalProperties: false,
130
+ },
131
+ ],
132
+ },
133
+ defaultOptions: [
134
+ {
135
+ valueObjectPatterns: ['Value', 'VO', 'ValueObject'],
136
+ requireReadonly: true,
137
+ checkObjectFreeze: true,
138
+ },
139
+ ],
140
+ create(context, [options = {}]) {
141
+ const { valueObjectPatterns = ['Value', 'VO', 'ValueObject'], requireReadonly = true, checkObjectFreeze = true, } = options || {};
142
+ /**
143
+ * Check class declarations
144
+ */
145
+ function checkClass(node) {
146
+ if (!node.id) {
147
+ return;
148
+ }
149
+ const className = node.id.name;
150
+ // Check if it's a value object
151
+ if (!isValueObject(className, valueObjectPatterns)) {
152
+ return;
153
+ }
154
+ // Check if Object.freeze is used
155
+ if (checkObjectFreeze && usesObjectFreeze(node)) {
156
+ return; // Already using Object.freeze
157
+ }
158
+ // Check properties for readonly
159
+ const mutableProperties = [];
160
+ for (const member of node.body.body) {
161
+ if (member.type === 'PropertyDefinition') {
162
+ if (requireReadonly && !hasReadonlyModifier(member)) {
163
+ mutableProperties.push(member);
164
+ }
165
+ }
166
+ }
167
+ if (mutableProperties.length > 0) {
168
+ context.report({
169
+ node,
170
+ messageId: 'mutableValueObject',
171
+ data: {
172
+ className,
173
+ },
174
+ suggest: [
175
+ {
176
+ messageId: 'addReadonly',
177
+ fix: () => null, // Complex fix, requires AST manipulation
178
+ },
179
+ {
180
+ messageId: 'useObjectFreeze',
181
+ fix: () => null,
182
+ },
183
+ {
184
+ messageId: 'makeImmutable',
185
+ fix: () => null,
186
+ },
187
+ ],
188
+ });
189
+ }
190
+ }
191
+ return {
192
+ ClassDeclaration: checkClass,
193
+ ClassExpression: checkClass,
194
+ };
195
+ },
196
+ });
197
+ //# sourceMappingURL=ddd-value-object-immutability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ddd-value-object-immutability.js","sourceRoot":"","sources":["../../../../../packages/eslint-plugin-modularity/src/rules/ddd-value-object-immutability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright (c) 2025 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * ESLint Rule: enforce-naming
8
+ * Enforce domain-specific naming conventions with business context
9
+ */
10
+ import type { TSESLint } from '@interlace/eslint-devkit';
11
+ type MessageIds = 'wrongTerminology' | 'useDomainTerm' | 'viewGlossary';
12
+ interface DomainTerm {
13
+ incorrect: string | RegExp;
14
+ correct: string;
15
+ context: string;
16
+ examples?: string[];
17
+ }
18
+ export interface Options {
19
+ /** Domain context for naming conventions (e.g., 'ecommerce', 'healthcare') */
20
+ domain?: string;
21
+ /** Array of domain-specific terminology rules */
22
+ terms?: DomainTerm[];
23
+ /** URL to domain glossary documentation */
24
+ glossaryUrl?: string;
25
+ }
26
+ type RuleOptions = [Options?];
27
+ export declare const enforceNaming: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
28
+ name: string;
29
+ };
30
+ export {};
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 Ofri Peretz
4
+ * Licensed under the MIT License. Use of this source code is governed by the
5
+ * MIT license that can be found in the LICENSE file.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.enforceNaming = void 0;
9
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
+ const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
+ exports.enforceNaming = (0, eslint_devkit_2.createRule)({
12
+ name: 'enforce-naming',
13
+ meta: {
14
+ type: 'suggestion',
15
+ docs: {
16
+ description: 'Enforce domain-specific naming conventions with business context',
17
+ },
18
+ fixable: 'code',
19
+ hasSuggestions: true,
20
+ messages: {
21
+ // 🎯 Token optimization: 46% reduction (56→30 tokens) - domain terminology keeps code clear
22
+ wrongTerminology: (0, eslint_devkit_1.formatLLMMessage)({
23
+ icon: eslint_devkit_1.MessageIcons.QUALITY,
24
+ issueName: 'Domain terminology mismatch',
25
+ cwe: 'CWE-216',
26
+ description: 'Domain terminology mismatch',
27
+ severity: 'MEDIUM',
28
+ fix: 'Use "{{correctTerm}}" ({{context}}) for ubiquitous language alignment',
29
+ documentationLink: 'Domain glossary',
30
+ }),
31
+ useDomainTerm: (0, eslint_devkit_1.formatLLMMessage)({
32
+ icon: eslint_devkit_1.MessageIcons.INFO,
33
+ issueName: 'Use Domain Term',
34
+ description: 'Replace with domain terminology',
35
+ severity: 'LOW',
36
+ fix: 'Use "{{correctTerm}}" instead',
37
+ documentationLink: 'https://martinfowler.com/bliki/UbiquitousLanguage.html',
38
+ }),
39
+ viewGlossary: (0, eslint_devkit_1.formatLLMMessage)({
40
+ icon: eslint_devkit_1.MessageIcons.INFO,
41
+ issueName: 'View Glossary',
42
+ description: 'View domain glossary',
43
+ severity: 'LOW',
44
+ fix: 'Reference domain glossary for correct terminology',
45
+ documentationLink: 'https://martinfowler.com/bliki/UbiquitousLanguage.html',
46
+ }),
47
+ },
48
+ schema: [
49
+ {
50
+ type: 'object',
51
+ properties: {
52
+ domain: {
53
+ type: 'string',
54
+ default: 'general',
55
+ },
56
+ terms: {
57
+ type: 'array',
58
+ items: {
59
+ type: 'object',
60
+ properties: {
61
+ incorrect: { type: ['string', 'object'] }, // string or regex
62
+ correct: { type: 'string' },
63
+ context: { type: 'string' },
64
+ examples: {
65
+ type: 'array',
66
+ items: { type: 'string' },
67
+ },
68
+ },
69
+ required: ['incorrect', 'correct', 'context'],
70
+ },
71
+ },
72
+ glossaryUrl: {
73
+ type: 'string',
74
+ },
75
+ },
76
+ additionalProperties: false,
77
+ },
78
+ ],
79
+ },
80
+ defaultOptions: [
81
+ {
82
+ domain: 'general',
83
+ terms: [],
84
+ glossaryUrl: undefined,
85
+ },
86
+ ],
87
+ create(context) {
88
+ const options = context.options[0] || {};
89
+ const { domain = 'general', terms = [] } = options || {};
90
+ // Early return if no terms configured
91
+ if (!terms || terms.length === 0) {
92
+ return {};
93
+ }
94
+ /**
95
+ * Check if identifier violates domain terms
96
+ */
97
+ const checkIdentifier = (name) => {
98
+ for (const term of terms) {
99
+ if (typeof term.incorrect === 'string') {
100
+ if (name.toLowerCase().includes(term.incorrect.toLowerCase())) {
101
+ return term;
102
+ }
103
+ }
104
+ else if (term.incorrect instanceof RegExp) {
105
+ if (term.incorrect.test(name)) {
106
+ return term;
107
+ }
108
+ }
109
+ }
110
+ return null;
111
+ };
112
+ /**
113
+ * Preserve case when replacing term
114
+ * e.g., User -> Customer, user -> customer, USER -> CUSTOMER
115
+ */
116
+ const preserveCase = (original, replacement) => {
117
+ if (original === original.toUpperCase()) {
118
+ return replacement.toUpperCase();
119
+ }
120
+ if (original[0] === original[0].toUpperCase()) {
121
+ return replacement.charAt(0).toUpperCase() + replacement.slice(1);
122
+ }
123
+ return replacement.toLowerCase();
124
+ };
125
+ /**
126
+ * Generate replacement suggestion
127
+ */
128
+ const generateReplacement = (name, term) => {
129
+ if (typeof term.incorrect === 'string') {
130
+ // Case-preserving replacement
131
+ return name.replace(new RegExp(`(${term.incorrect})`, 'gi'), (match) => {
132
+ return preserveCase(match, term.correct);
133
+ });
134
+ }
135
+ // For regex, just suggest the correct term
136
+ return term.correct;
137
+ };
138
+ /**
139
+ * Report violation
140
+ */
141
+ const reportViolation = (node, violatedTerm) => {
142
+ const replacement = generateReplacement(node.name, violatedTerm);
143
+ context.report({
144
+ node,
145
+ messageId: 'wrongTerminology',
146
+ data: {
147
+ incorrectTerm: node.name,
148
+ correctTerm: violatedTerm.correct,
149
+ context: violatedTerm.context || domain,
150
+ domain,
151
+ },
152
+ suggest: [
153
+ {
154
+ messageId: 'useDomainTerm',
155
+ data: { correctTerm: violatedTerm.correct },
156
+ fix: (fixer) => {
157
+ return fixer.replaceText(node, replacement);
158
+ },
159
+ },
160
+ ],
161
+ });
162
+ };
163
+ return {
164
+ // Check variable declarations
165
+ VariableDeclarator(node) {
166
+ if (node.id.type !== 'Identifier')
167
+ return;
168
+ const violatedTerm = checkIdentifier(node.id.name);
169
+ if (violatedTerm) {
170
+ reportViolation(node.id, violatedTerm);
171
+ }
172
+ },
173
+ // Check function declarations
174
+ FunctionDeclaration(node) {
175
+ if (!node.id)
176
+ return;
177
+ const violatedTerm = checkIdentifier(node.id.name);
178
+ if (violatedTerm) {
179
+ reportViolation(node.id, violatedTerm);
180
+ }
181
+ },
182
+ // Check class declarations
183
+ ClassDeclaration(node) {
184
+ if (!node.id)
185
+ return;
186
+ const violatedTerm = checkIdentifier(node.id.name);
187
+ if (violatedTerm) {
188
+ reportViolation(node.id, violatedTerm);
189
+ }
190
+ },
191
+ // Check property definitions
192
+ PropertyDefinition(node) {
193
+ if (node.key.type !== 'Identifier')
194
+ return;
195
+ const violatedTerm = checkIdentifier(node.key.name);
196
+ if (violatedTerm) {
197
+ reportViolation(node.key, violatedTerm);
198
+ }
199
+ },
200
+ // Check method definitions
201
+ MethodDefinition(node) {
202
+ if (node.key.type !== 'Identifier')
203
+ return;
204
+ const violatedTerm = checkIdentifier(node.key.name);
205
+ if (violatedTerm) {
206
+ reportViolation(node.key, violatedTerm);
207
+ }
208
+ },
209
+ };
210
+ },
211
+ });
212
+ //# sourceMappingURL=enforce-naming.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforce-naming.js","sourceRoot":"","sources":["../../../../../packages/eslint-plugin-modularity/src/rules/enforce-naming.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright (c) 2025 Ofri Peretz
3
+ * Licensed under the MIT License. Use of this source code is governed by the
4
+ * MIT license that can be found in the LICENSE file.
5
+ */
6
+ /**
7
+ * ESLint Rule: enforce-rest-conventions
8
+ * Validates REST endpoint design against best practices
9
+ * Priority 6: API Design & Evolution
10
+ *
11
+ * @see https://restfulapi.net/
12
+ */
13
+ import type { TSESLint } from '@interlace/eslint-devkit';
14
+ type MessageIds = 'restConventionViolation' | 'useProperHttpMethod' | 'useProperStatusCode' | 'fixResourceNaming';
15
+ export interface Options {
16
+ /** Check HTTP methods. Default: true */
17
+ checkHttpMethods?: boolean;
18
+ /** Check status codes. Default: true */
19
+ checkStatusCodes?: boolean;
20
+ /** Check resource naming. Default: true */
21
+ checkResourceNaming?: boolean;
22
+ }
23
+ type RuleOptions = [Options?];
24
+ export declare const enforceRestConventions: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
25
+ name: string;
26
+ };
27
+ export {};
@@ -0,0 +1,162 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) 2025 Ofri Peretz
4
+ * Licensed under the MIT License. Use of this source code is governed by the
5
+ * MIT license that can be found in the LICENSE file.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.enforceRestConventions = void 0;
9
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
+ const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
+ /**
12
+ * Valid HTTP methods for REST
13
+ */
14
+ const VALID_HTTP_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
15
+ /**
16
+ * Check if HTTP method is valid
17
+ */
18
+ function isValidHttpMethod(method) {
19
+ return VALID_HTTP_METHODS.includes(method.toUpperCase());
20
+ }
21
+ exports.enforceRestConventions = (0, eslint_devkit_2.createRule)({
22
+ name: 'enforce-rest-conventions',
23
+ meta: {
24
+ type: 'suggestion',
25
+ docs: {
26
+ description: 'Validates REST endpoint design against best practices',
27
+ },
28
+ messages: {
29
+ restConventionViolation: (0, eslint_devkit_1.formatLLMMessage)({
30
+ icon: eslint_devkit_1.MessageIcons.ARCHITECTURE,
31
+ issueName: 'REST convention violation',
32
+ description: '{{violation}}: {{details}}',
33
+ severity: 'MEDIUM',
34
+ fix: 'Follow RESTful conventions for HTTP methods, status codes, and resource naming',
35
+ documentationLink: 'https://restfulapi.net/',
36
+ }),
37
+ useProperHttpMethod: (0, eslint_devkit_1.formatLLMMessage)({
38
+ icon: eslint_devkit_1.MessageIcons.INFO,
39
+ issueName: 'Use HTTP Method',
40
+ description: 'Use proper HTTP method',
41
+ severity: 'LOW',
42
+ fix: 'GET (read), POST (create), PUT/PATCH (update), DELETE (delete)',
43
+ documentationLink: 'https://restfulapi.net/http-methods/',
44
+ }),
45
+ useProperStatusCode: (0, eslint_devkit_1.formatLLMMessage)({
46
+ icon: eslint_devkit_1.MessageIcons.INFO,
47
+ issueName: 'Use Status Code',
48
+ description: 'Use appropriate status code',
49
+ severity: 'LOW',
50
+ fix: '200 (OK), 201 (Created), 204 (No Content), 400 (Bad Request), 404 (Not Found)',
51
+ documentationLink: 'https://restfulapi.net/http-status-codes/',
52
+ }),
53
+ fixResourceNaming: (0, eslint_devkit_1.formatLLMMessage)({
54
+ icon: eslint_devkit_1.MessageIcons.INFO,
55
+ issueName: 'Resource Naming',
56
+ description: 'Use plural nouns for resources',
57
+ severity: 'LOW',
58
+ fix: '/users, /orders, /products (not /user, /order)',
59
+ documentationLink: 'https://restfulapi.net/resource-naming/',
60
+ }),
61
+ },
62
+ schema: [
63
+ {
64
+ type: 'object',
65
+ properties: {
66
+ checkHttpMethods: {
67
+ type: 'boolean',
68
+ default: true,
69
+ description: 'Check HTTP methods',
70
+ },
71
+ checkStatusCodes: {
72
+ type: 'boolean',
73
+ default: true,
74
+ description: 'Check status codes',
75
+ },
76
+ checkResourceNaming: {
77
+ type: 'boolean',
78
+ default: true,
79
+ description: 'Check resource naming',
80
+ },
81
+ },
82
+ additionalProperties: false,
83
+ },
84
+ ],
85
+ },
86
+ defaultOptions: [
87
+ {
88
+ checkHttpMethods: true,
89
+ checkStatusCodes: true,
90
+ checkResourceNaming: true,
91
+ },
92
+ ],
93
+ create(context, [options = {}]) {
94
+ const { checkHttpMethods = true, checkStatusCodes = true, checkResourceNaming = true, } = options || {};
95
+ /**
96
+ * Check CallExpression for REST API patterns
97
+ */
98
+ function checkCallExpression(node) {
99
+ // Check for Express/Fastify route handlers: app.get(), router.post(), etc.
100
+ if (node.callee.type === 'MemberExpression') {
101
+ const property = node.callee.property;
102
+ if (property.type === 'Identifier') {
103
+ const methodName = property.name.toLowerCase();
104
+ const httpMethods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'];
105
+ if (httpMethods.includes(methodName)) {
106
+ // Check HTTP method
107
+ if (checkHttpMethods && !isValidHttpMethod(methodName)) {
108
+ context.report({
109
+ node: property,
110
+ messageId: 'restConventionViolation',
111
+ data: {
112
+ violation: 'Invalid HTTP method',
113
+ details: `"${methodName}" is not a valid REST HTTP method`,
114
+ },
115
+ suggest: [
116
+ { messageId: 'useProperHttpMethod', fix: () => null },
117
+ ],
118
+ });
119
+ }
120
+ // Check resource naming (first argument should be a path)
121
+ if (checkResourceNaming && node.arguments.length > 0) {
122
+ const pathArg = node.arguments[0];
123
+ if (pathArg.type === 'Literal' && typeof pathArg.value === 'string') {
124
+ const path = pathArg.value;
125
+ // Check if path uses plural nouns (basic check)
126
+ if (path.startsWith('/') && path.length > 1) {
127
+ const resource = path.split('/')[1];
128
+ // Basic check: resource should be plural or have :id
129
+ if (!path.includes(':') && !resource.endsWith('s') && resource.length > 3) {
130
+ context.report({
131
+ node: pathArg,
132
+ messageId: 'restConventionViolation',
133
+ data: {
134
+ violation: 'Resource naming',
135
+ details: `Resource "${resource}" should be plural (e.g., /users, /orders)`,
136
+ },
137
+ suggest: [
138
+ { messageId: 'fixResourceNaming', fix: () => null },
139
+ ],
140
+ });
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ }
147
+ }
148
+ // Check for status code usage: res.status(200)
149
+ if (checkStatusCodes && node.callee.type === 'MemberExpression') {
150
+ const property = node.callee.property;
151
+ if (property.type === 'Identifier' && property.name === 'status') {
152
+ // Check if status code is appropriate (would need to track HTTP method)
153
+ // This is simplified - full implementation would track the HTTP method from parent
154
+ }
155
+ }
156
+ }
157
+ return {
158
+ CallExpression: checkCallExpression,
159
+ };
160
+ },
161
+ });
162
+ //# sourceMappingURL=enforce-rest-conventions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enforce-rest-conventions.js","sourceRoot":"","sources":["../../../../../packages/eslint-plugin-modularity/src/rules/enforce-rest-conventions.ts"],"names":[],"mappings":"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"}