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,23 @@
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: no-external-api-calls-in-utils
8
+ * Detects network calls in utility functions
9
+ *
10
+ * @see https://rules.sonarsource.com/javascript/RSPEC-1075/
11
+ */
12
+ import type { TSESLint } from '@interlace/eslint-devkit';
13
+ type MessageIds = 'externalApiCallInUtils' | 'useDependencyInjection' | 'extractToService' | 'passApiClient';
14
+ export interface Options {
15
+ ignoreInTests?: boolean;
16
+ networkMethods?: string[];
17
+ utilityPatterns?: string[];
18
+ }
19
+ type RuleOptions = [Options?];
20
+ export declare const noExternalApiCallsInUtils: TSESLint.RuleModule<MessageIds, RuleOptions, unknown, TSESLint.RuleListener> & {
21
+ name: string;
22
+ };
23
+ export {};
@@ -0,0 +1,151 @@
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.noExternalApiCallsInUtils = void 0;
9
+ const eslint_devkit_1 = require("@interlace/eslint-devkit");
10
+ const eslint_devkit_2 = require("@interlace/eslint-devkit");
11
+ /**
12
+ * Check if file is a utility file
13
+ */
14
+ function isUtilityFile(filename, patterns) {
15
+ return patterns.some(pattern => {
16
+ const regex = new RegExp(pattern.replace(/\*\*/g, '.*').replace(/\*/g, '[^/]*'));
17
+ return regex.test(filename);
18
+ });
19
+ }
20
+ /**
21
+ * Check if call is a network call
22
+ */
23
+ function isNetworkCall(node, networkMethods) {
24
+ if (node.callee.type === 'Identifier') {
25
+ return networkMethods.includes(node.callee.name);
26
+ }
27
+ if (node.callee.type === 'MemberExpression' &&
28
+ node.callee.property.type === 'Identifier') {
29
+ const methodName = node.callee.property.name;
30
+ const objectName = node.callee.object.type === 'Identifier'
31
+ ? node.callee.object.name
32
+ : '';
33
+ return networkMethods.includes(methodName) ||
34
+ networkMethods.includes(`${objectName}.${methodName}`);
35
+ }
36
+ return false;
37
+ }
38
+ exports.noExternalApiCallsInUtils = (0, eslint_devkit_2.createRule)({
39
+ name: 'no-external-api-calls-in-utils',
40
+ meta: {
41
+ type: 'problem',
42
+ docs: {
43
+ description: 'Detects network calls in utility functions',
44
+ },
45
+ hasSuggestions: true,
46
+ messages: {
47
+ externalApiCallInUtils: (0, eslint_devkit_1.formatLLMMessage)({
48
+ icon: eslint_devkit_1.MessageIcons.ARCHITECTURE,
49
+ issueName: 'External API call in utils',
50
+ description: 'Network call in utility function - breaks testability',
51
+ severity: 'HIGH',
52
+ fix: 'Use dependency injection for network calls',
53
+ documentationLink: 'https://rules.sonarsource.com/javascript/RSPEC-1075/',
54
+ }),
55
+ useDependencyInjection: (0, eslint_devkit_1.formatLLMMessage)({
56
+ icon: eslint_devkit_1.MessageIcons.INFO,
57
+ issueName: 'Use Dependency Injection',
58
+ description: 'Inject API client',
59
+ severity: 'LOW',
60
+ fix: 'function util(apiClient) { return apiClient.get(...) }',
61
+ documentationLink: 'https://en.wikipedia.org/wiki/Dependency_injection',
62
+ }),
63
+ extractToService: (0, eslint_devkit_1.formatLLMMessage)({
64
+ icon: eslint_devkit_1.MessageIcons.INFO,
65
+ issueName: 'Extract to Service',
66
+ description: 'Extract to service layer',
67
+ severity: 'LOW',
68
+ fix: 'Create services/apiService.ts',
69
+ documentationLink: 'https://martinfowler.com/eaaCatalog/serviceLayer.html',
70
+ }),
71
+ passApiClient: (0, eslint_devkit_1.formatLLMMessage)({
72
+ icon: eslint_devkit_1.MessageIcons.INFO,
73
+ issueName: 'Pass API Client',
74
+ description: 'Pass API client as parameter',
75
+ severity: 'LOW',
76
+ fix: 'function util(apiClient, data) { ... }',
77
+ documentationLink: 'https://en.wikipedia.org/wiki/Dependency_injection',
78
+ }),
79
+ },
80
+ schema: [
81
+ {
82
+ type: 'object',
83
+ properties: {
84
+ ignoreInTests: {
85
+ type: 'boolean',
86
+ default: true,
87
+ },
88
+ networkMethods: {
89
+ type: 'array',
90
+ items: { type: 'string' },
91
+ default: ['fetch', 'axios', 'request', 'http.get', 'https.get', 'get', 'post', 'put', 'delete', 'patch'],
92
+ },
93
+ utilityPatterns: {
94
+ type: 'array',
95
+ items: { type: 'string' },
96
+ default: ['**/utils/**', '**/helpers/**', '**/lib/**'],
97
+ },
98
+ },
99
+ additionalProperties: false,
100
+ },
101
+ ],
102
+ },
103
+ defaultOptions: [
104
+ {
105
+ ignoreInTests: true,
106
+ networkMethods: ['fetch', 'axios', 'request', 'http.get', 'https.get', 'get', 'post', 'put', 'delete', 'patch'],
107
+ utilityPatterns: ['**/utils/**', '**/helpers/**', '**/lib/**'],
108
+ },
109
+ ],
110
+ create(context, [options = {}]) {
111
+ const { ignoreInTests = true, networkMethods = ['fetch', 'axios', 'request', 'http.get', 'https.get', 'get', 'post', 'put', 'delete', 'patch'], utilityPatterns = ['**/utils/**', '**/helpers/**', '**/lib/**'], } = options || {};
112
+ const filename = context.getFilename();
113
+ const isTestFile = ignoreInTests && /\.(test|spec)\.(ts|tsx|js|jsx)$/.test(filename);
114
+ if (isTestFile) {
115
+ return {};
116
+ }
117
+ const isUtility = isUtilityFile(filename, utilityPatterns);
118
+ if (!isUtility) {
119
+ return {};
120
+ }
121
+ /**
122
+ * Check for network calls
123
+ */
124
+ function checkCallExpression(node) {
125
+ if (isNetworkCall(node, networkMethods)) {
126
+ context.report({
127
+ node,
128
+ messageId: 'externalApiCallInUtils',
129
+ suggest: [
130
+ {
131
+ messageId: 'useDependencyInjection',
132
+ fix: () => null,
133
+ },
134
+ {
135
+ messageId: 'extractToService',
136
+ fix: () => null,
137
+ },
138
+ {
139
+ messageId: 'passApiClient',
140
+ fix: () => null,
141
+ },
142
+ ],
143
+ });
144
+ }
145
+ }
146
+ return {
147
+ CallExpression: checkCallExpression,
148
+ };
149
+ },
150
+ });
151
+ //# sourceMappingURL=no-external-api-calls-in-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"no-external-api-calls-in-utils.js","sourceRoot":"","sources":["../../../../../packages/eslint-plugin-modularity/src/rules/no-external-api-calls-in-utils.ts"],"names":[],"mappings":"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"}