@sun-asterisk/sunlint 1.3.0 → 1.3.2
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.
- package/CHANGELOG.md +115 -1
- package/CONTRIBUTING.md +249 -605
- package/README.md +3 -4
- package/config/ci-cd.json +54 -0
- package/config/development.json +56 -0
- package/config/large-project.json +143 -0
- package/config/presets/all.json +0 -1
- package/config/release.json +70 -0
- package/config/rule-analysis-strategies.js +38 -3
- package/config/rules/enhanced-rules-registry.json +474 -1179
- package/config/rules/rules-registry-generated.json +3 -3
- package/core/cli-action-handler.js +24 -30
- package/core/cli-program.js +11 -3
- package/core/config-merger.js +29 -2
- package/core/enhanced-rules-registry.js +3 -2
- package/core/semantic-engine.js +129 -19
- package/core/semantic-rule-base.js +4 -2
- package/core/unified-rule-registry.js +1 -1
- package/docs/COMMAND-EXAMPLES.md +134 -0
- package/docs/LARGE-PROJECT-GUIDE.md +324 -0
- package/engines/heuristic-engine.js +135 -16
- package/integrations/eslint/plugin/index.js +0 -2
- package/integrations/eslint/plugin/rules/common/c003-no-vague-abbreviations.js +59 -1
- package/integrations/eslint/plugin/rules/common/c006-function-name-verb-noun.js +26 -1
- package/integrations/eslint/plugin/rules/common/c030-use-custom-error-classes.js +54 -19
- package/origin-rules/common-en.md +19 -15
- package/package.json +1 -1
- package/rules/common/C002_no_duplicate_code/analyzer.js +334 -36
- package/rules/common/C003_no_vague_abbreviations/analyzer.js +220 -35
- package/rules/common/C006_function_naming/analyzer.js +29 -3
- package/rules/common/C010_limit_block_nesting/analyzer.js +181 -337
- package/rules/common/C010_limit_block_nesting/config.json +64 -0
- package/rules/common/C010_limit_block_nesting/regex-based-analyzer.js +379 -0
- package/rules/common/C010_limit_block_nesting/symbol-based-analyzer.js +231 -0
- package/rules/common/C013_no_dead_code/analyzer.js +75 -177
- package/rules/common/C013_no_dead_code/config.json +61 -0
- package/rules/common/C013_no_dead_code/regex-based-analyzer.js +345 -0
- package/rules/common/C013_no_dead_code/symbol-based-analyzer.js +640 -0
- package/rules/common/C014_dependency_injection/analyzer.js +48 -313
- package/rules/common/C014_dependency_injection/config.json +26 -0
- package/rules/common/C014_dependency_injection/symbol-based-analyzer.js +751 -0
- package/rules/common/C017_constructor_logic/analyzer.js +254 -17
- package/rules/common/C017_constructor_logic/semantic-analyzer.js +340 -0
- package/rules/common/C018_no_throw_generic_error/analyzer.js +232 -0
- package/rules/common/C018_no_throw_generic_error/config.json +50 -0
- package/rules/common/C018_no_throw_generic_error/regex-based-analyzer.js +387 -0
- package/rules/common/C018_no_throw_generic_error/symbol-based-analyzer.js +314 -0
- package/rules/common/C019_log_level_usage/analyzer.js +110 -317
- package/rules/common/C019_log_level_usage/pattern-analyzer.js +88 -0
- package/rules/common/C019_log_level_usage/system-log-analyzer.js +1267 -0
- package/rules/common/C023_no_duplicate_variable/analyzer.js +180 -0
- package/rules/common/C023_no_duplicate_variable/config.json +50 -0
- package/rules/common/C023_no_duplicate_variable/symbol-based-analyzer.js +158 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/analyzer.js +180 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/config.json +50 -0
- package/rules/common/C024_no_scatter_hardcoded_constants/symbol-based-analyzer.js +181 -0
- package/rules/common/C030_use_custom_error_classes/analyzer.js +200 -0
- package/rules/common/C033_separate_service_repository/README.md +78 -0
- package/rules/common/C033_separate_service_repository/analyzer.js +160 -0
- package/rules/common/C033_separate_service_repository/config.json +50 -0
- package/rules/common/C033_separate_service_repository/regex-based-analyzer.js +585 -0
- package/rules/common/C033_separate_service_repository/symbol-based-analyzer.js +368 -0
- package/rules/common/C035_error_logging_context/STRATEGY.md +99 -0
- package/rules/common/C035_error_logging_context/analyzer.js +232 -0
- package/rules/common/C035_error_logging_context/config.json +54 -0
- package/rules/common/C035_error_logging_context/regex-based-analyzer.js +299 -0
- package/rules/common/C035_error_logging_context/symbol-based-analyzer.js +454 -0
- package/rules/common/C040_centralized_validation/analyzer.js +165 -0
- package/rules/common/C040_centralized_validation/config.json +46 -0
- package/rules/common/C040_centralized_validation/regex-based-analyzer.js +243 -0
- package/rules/common/C040_centralized_validation/symbol-based-analyzer.js +416 -0
- package/rules/common/{C076_single_test_behavior → C072_single_test_behavior}/analyzer.js +6 -6
- package/rules/common/C076_explicit_function_types/README.md +30 -0
- package/rules/common/C076_explicit_function_types/analyzer.js +172 -0
- package/rules/common/C076_explicit_function_types/config.json +15 -0
- package/rules/common/C076_explicit_function_types/semantic-analyzer.js +341 -0
- package/rules/index.js +6 -1
- package/rules/parser/rule-parser.js +13 -2
- package/rules/security/S005_no_origin_auth/README.md +226 -0
- package/rules/security/S005_no_origin_auth/analyzer.js +184 -0
- package/rules/security/S005_no_origin_auth/ast-analyzer.js +406 -0
- package/rules/security/S005_no_origin_auth/config.json +85 -0
- package/rules/security/S006_no_plaintext_recovery_codes/README.md +139 -0
- package/rules/security/S006_no_plaintext_recovery_codes/analyzer.js +306 -0
- package/rules/security/S006_no_plaintext_recovery_codes/config.json +48 -0
- package/rules/security/S007_no_plaintext_otp/README.md +198 -0
- package/rules/security/S007_no_plaintext_otp/analyzer.js +406 -0
- package/rules/security/S007_no_plaintext_otp/config.json +79 -0
- package/rules/security/S007_no_plaintext_otp/semantic-analyzer.js +609 -0
- package/rules/security/S007_no_plaintext_otp/semantic-config.json +195 -0
- package/rules/security/S007_no_plaintext_otp/semantic-wrapper.js +280 -0
- package/rules/security/S009_no_insecure_encryption/README.md +158 -0
- package/rules/security/S009_no_insecure_encryption/analyzer.js +319 -0
- package/rules/security/S009_no_insecure_encryption/config.json +55 -0
- package/rules/security/S010_no_insecure_encryption/README.md +224 -0
- package/rules/security/S010_no_insecure_encryption/analyzer.js +493 -0
- package/rules/security/S010_no_insecure_encryption/config.json +48 -0
- package/rules/security/S016_no_sensitive_querystring/STRATEGY.md +149 -0
- package/rules/security/S016_no_sensitive_querystring/analyzer.js +276 -0
- package/rules/security/S016_no_sensitive_querystring/config.json +127 -0
- package/rules/security/S016_no_sensitive_querystring/regex-based-analyzer.js +258 -0
- package/rules/security/S016_no_sensitive_querystring/symbol-based-analyzer.js +495 -0
- package/rules/security/S027_no_hardcoded_secrets/analyzer.js +180 -366
- package/rules/security/S027_no_hardcoded_secrets/categories.json +153 -0
- package/rules/security/S027_no_hardcoded_secrets/categorized-analyzer.js +250 -0
- package/rules/security/S048_no_current_password_in_reset/README.md +222 -0
- package/rules/security/S048_no_current_password_in_reset/analyzer.js +366 -0
- package/rules/security/S048_no_current_password_in_reset/config.json +48 -0
- package/rules/security/S055_content_type_validation/README.md +176 -0
- package/rules/security/S055_content_type_validation/analyzer.js +312 -0
- package/rules/security/S055_content_type_validation/config.json +48 -0
- package/rules/utils/rule-helpers.js +140 -1
- package/scripts/consolidate-config.js +116 -0
- package/scripts/prepare-release.sh +1 -1
- package/config/rules/rules-registry.json +0 -765
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +0 -392
- package/docs/FUTURE_PACKAGES.md +0 -83
- package/docs/HEURISTIC_VS_AI.md +0 -113
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +0 -112
- package/docs/PRODUCTION_SIZE_IMPACT.md +0 -183
- package/docs/RELEASE_GUIDE.md +0 -230
- package/docs/STANDARDIZED-CATEGORY-FILTERING.md +0 -156
- package/integrations/eslint/plugin/rules/common/c076-single-behavior-per-test.js +0 -254
- package/rules/common/C006_function_naming/smart-analyzer.js +0 -503
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom ESLint rule for: C076 – Each test should assert only one behavior
|
|
3
|
-
* Rule ID: custom/c076
|
|
4
|
-
* Purpose: Ensure test functions focus on testing a single behavior/aspect
|
|
5
|
-
* Note: Similar to C072 but with broader scope and different focus
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
module.exports = {
|
|
9
|
-
meta: {
|
|
10
|
-
type: "suggestion",
|
|
11
|
-
docs: {
|
|
12
|
-
description: "Each test should assert only one behavior to maintain focus and clarity",
|
|
13
|
-
recommended: true,
|
|
14
|
-
category: "Testing"
|
|
15
|
-
},
|
|
16
|
-
schema: [
|
|
17
|
-
{
|
|
18
|
-
type: "object",
|
|
19
|
-
properties: {
|
|
20
|
-
maxAssertions: {
|
|
21
|
-
type: "integer",
|
|
22
|
-
minimum: 1,
|
|
23
|
-
default: 1
|
|
24
|
-
},
|
|
25
|
-
allowSetupAssertions: {
|
|
26
|
-
type: "boolean",
|
|
27
|
-
default: true
|
|
28
|
-
},
|
|
29
|
-
assertionPatterns: {
|
|
30
|
-
type: "array",
|
|
31
|
-
items: {
|
|
32
|
-
type: "string"
|
|
33
|
-
},
|
|
34
|
-
default: ["expect", "assert", "should"]
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
additionalProperties: false
|
|
38
|
-
}
|
|
39
|
-
],
|
|
40
|
-
messages: {
|
|
41
|
-
tooManyAssertions: "Test '{{testName}}' has {{count}} assertions. Each test should focus on one behavior (max {{max}} assertions)",
|
|
42
|
-
multipleBehaviors: "Test '{{testName}}' appears to test multiple behaviors. Consider splitting into separate test cases"
|
|
43
|
-
}
|
|
44
|
-
},
|
|
45
|
-
create(context) {
|
|
46
|
-
const options = context.options[0] || {};
|
|
47
|
-
const maxAssertions = options.maxAssertions || 1;
|
|
48
|
-
const allowSetupAssertions = options.allowSetupAssertions !== false;
|
|
49
|
-
const assertionPatterns = options.assertionPatterns || ["expect", "assert", "should"];
|
|
50
|
-
|
|
51
|
-
function isTestFunction(node) {
|
|
52
|
-
if (!node || !node.callee) return false;
|
|
53
|
-
|
|
54
|
-
if (node.type === "CallExpression") {
|
|
55
|
-
// Direct test/it/describe calls
|
|
56
|
-
if (node.callee.type === "Identifier") {
|
|
57
|
-
return ["test", "it", "describe", "context"].includes(node.callee.name);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Method calls like jest.test, mocha.it, etc.
|
|
61
|
-
if (node.callee.type === "MemberExpression" &&
|
|
62
|
-
node.callee.property &&
|
|
63
|
-
["test", "it", "describe", "context"].includes(node.callee.property.name)) {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return false;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function isSetupFunction(node) {
|
|
71
|
-
if (!node || !node.callee) return false;
|
|
72
|
-
|
|
73
|
-
const setupFunctions = ["beforeEach", "afterEach", "beforeAll", "afterAll", "before", "after"];
|
|
74
|
-
|
|
75
|
-
if (node.type === "CallExpression") {
|
|
76
|
-
if (node.callee.type === "Identifier") {
|
|
77
|
-
return setupFunctions.includes(node.callee.name);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (node.callee.type === "MemberExpression" &&
|
|
81
|
-
node.callee.property &&
|
|
82
|
-
setupFunctions.includes(node.callee.property.name)) {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function isAssertionCall(node) {
|
|
90
|
-
if (!node || node.type !== "CallExpression" || !node.callee) {
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Direct assertion calls
|
|
95
|
-
if (node.callee.type === "Identifier") {
|
|
96
|
-
return assertionPatterns.includes(node.callee.name);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Method calls like chai.expect, jest.expect, etc.
|
|
100
|
-
if (node.callee.type === "MemberExpression" && node.callee.property) {
|
|
101
|
-
return assertionPatterns.includes(node.callee.property.name);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function countAssertions(testBody, testName) {
|
|
108
|
-
let assertionCount = 0;
|
|
109
|
-
let setupAssertionCount = 0;
|
|
110
|
-
let hasMultipleBehaviorIndicators = false;
|
|
111
|
-
|
|
112
|
-
function traverse(node) {
|
|
113
|
-
if (!node || typeof node !== 'object') return;
|
|
114
|
-
|
|
115
|
-
// Count assertions
|
|
116
|
-
if (isAssertionCall(node)) {
|
|
117
|
-
// Check if this assertion is in setup/teardown
|
|
118
|
-
let parent = node;
|
|
119
|
-
let inSetup = false;
|
|
120
|
-
while (parent && parent.parent) {
|
|
121
|
-
parent = parent.parent;
|
|
122
|
-
if (parent.type === "CallExpression" && isSetupFunction(parent)) {
|
|
123
|
-
inSetup = true;
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (inSetup) {
|
|
129
|
-
setupAssertionCount++;
|
|
130
|
-
} else {
|
|
131
|
-
assertionCount++;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Look for multiple behavior indicators
|
|
136
|
-
if (node.type === "CallExpression") {
|
|
137
|
-
// Multiple nested test functions indicate multiple behaviors
|
|
138
|
-
if (isTestFunction(node) && node !== testBody.parent) {
|
|
139
|
-
hasMultipleBehaviorIndicators = true;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Look for comment patterns that suggest multiple behaviors
|
|
144
|
-
if (node.type === "ExpressionStatement" && node.leadingComments) {
|
|
145
|
-
const comments = node.leadingComments.map(c => c.value.toLowerCase());
|
|
146
|
-
const behaviorKeywords = ["and", "also", "then", "next", "additionally", "furthermore"];
|
|
147
|
-
if (comments.some(comment =>
|
|
148
|
-
behaviorKeywords.some(keyword => comment.includes(keyword)))) {
|
|
149
|
-
hasMultipleBehaviorIndicators = true;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Recursively check child nodes, but don't go into nested test functions
|
|
154
|
-
for (const key in node) {
|
|
155
|
-
if (key === 'parent' || key === 'range' || key === 'loc') continue;
|
|
156
|
-
|
|
157
|
-
const child = node[key];
|
|
158
|
-
if (Array.isArray(child)) {
|
|
159
|
-
child.forEach(item => {
|
|
160
|
-
if (item && typeof item === 'object' && item.type) {
|
|
161
|
-
if (!(item.type === "CallExpression" && isTestFunction(item))) {
|
|
162
|
-
traverse(item);
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
} else if (child && typeof child === 'object' && child.type) {
|
|
167
|
-
if (!(child.type === "CallExpression" && isTestFunction(child))) {
|
|
168
|
-
traverse(child);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
traverse(testBody);
|
|
175
|
-
|
|
176
|
-
return {
|
|
177
|
-
assertions: assertionCount,
|
|
178
|
-
setupAssertions: setupAssertionCount,
|
|
179
|
-
hasMultipleBehaviors: hasMultipleBehaviorIndicators
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
CallExpression(node) {
|
|
185
|
-
// Only check test function calls, not describe blocks
|
|
186
|
-
if (!isTestFunction(node) || (node.callee.type === "Identifier" && node.callee.name === "describe")) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Skip describe blocks
|
|
191
|
-
if (node.callee.type === "Identifier" && ["describe", "context"].includes(node.callee.name)) {
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Must have test name and callback
|
|
196
|
-
if (!node.arguments || node.arguments.length < 2) return;
|
|
197
|
-
|
|
198
|
-
const testName = node.arguments[0];
|
|
199
|
-
const testCallback = node.arguments[1];
|
|
200
|
-
|
|
201
|
-
if (!testCallback ||
|
|
202
|
-
(testCallback.type !== "FunctionExpression" &&
|
|
203
|
-
testCallback.type !== "ArrowFunctionExpression")) {
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
const testNameStr = testName.type === "Literal" ? testName.value :
|
|
208
|
-
testName.type === "TemplateLiteral" ? "template" : "unnamed";
|
|
209
|
-
|
|
210
|
-
// Get function body
|
|
211
|
-
const fnBody = testCallback.body;
|
|
212
|
-
if (!fnBody) return;
|
|
213
|
-
|
|
214
|
-
// Handle both block statements and expression bodies
|
|
215
|
-
let bodyToCheck = fnBody;
|
|
216
|
-
if (testCallback.type === "ArrowFunctionExpression" && fnBody.type !== "BlockStatement") {
|
|
217
|
-
bodyToCheck = { type: "BlockStatement", body: [{ type: "ExpressionStatement", expression: fnBody }] };
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// Count assertions and analyze behavior
|
|
221
|
-
const analysis = countAssertions(bodyToCheck, testNameStr);
|
|
222
|
-
|
|
223
|
-
// Calculate effective assertions (exclude setup if allowed)
|
|
224
|
-
const effectiveAssertions = allowSetupAssertions ?
|
|
225
|
-
analysis.assertions :
|
|
226
|
-
analysis.assertions + analysis.setupAssertions;
|
|
227
|
-
|
|
228
|
-
// Report if too many assertions
|
|
229
|
-
if (effectiveAssertions > maxAssertions) {
|
|
230
|
-
context.report({
|
|
231
|
-
node,
|
|
232
|
-
messageId: "tooManyAssertions",
|
|
233
|
-
data: {
|
|
234
|
-
testName: testNameStr,
|
|
235
|
-
count: effectiveAssertions,
|
|
236
|
-
max: maxAssertions
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Report if multiple behaviors detected
|
|
242
|
-
if (analysis.hasMultipleBehaviors) {
|
|
243
|
-
context.report({
|
|
244
|
-
node,
|
|
245
|
-
messageId: "multipleBehaviors",
|
|
246
|
-
data: {
|
|
247
|
-
testName: testNameStr
|
|
248
|
-
}
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
};
|