@sun-asterisk/sunlint 1.0.5
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 +202 -0
- package/LICENSE +21 -0
- package/README.md +490 -0
- package/cli-legacy.js +355 -0
- package/cli.js +35 -0
- package/config/default.json +22 -0
- package/config/presets/beginner.json +36 -0
- package/config/presets/ci.json +46 -0
- package/config/presets/recommended.json +24 -0
- package/config/presets/strict.json +32 -0
- package/config/rules-registry.json +681 -0
- package/config/sunlint-schema.json +166 -0
- package/config/typescript/custom-rules-new.js +0 -0
- package/config/typescript/custom-rules.js +9 -0
- package/config/typescript/eslint.config.js +110 -0
- package/config/typescript/package-lock.json +1585 -0
- package/config/typescript/package.json +13 -0
- package/config/typescript/security-rules/index.js +90 -0
- package/config/typescript/security-rules/s005-no-origin-auth.js +95 -0
- package/config/typescript/security-rules/s006-activation-recovery-secret-not-plaintext.js +69 -0
- package/config/typescript/security-rules/s008-crypto-agility.js +62 -0
- package/config/typescript/security-rules/s009-no-insecure-crypto.js +103 -0
- package/config/typescript/security-rules/s010-no-insecure-random-in-sensitive-context.js +123 -0
- package/config/typescript/security-rules/s011-no-insecure-uuid.js +66 -0
- package/config/typescript/security-rules/s012-hardcode-secret.js +71 -0
- package/config/typescript/security-rules/s014-insecure-tls-version.js +50 -0
- package/config/typescript/security-rules/s015-insecure-tls-certificate.js +43 -0
- package/config/typescript/security-rules/s016-sensitive-query-parameter.js +59 -0
- package/config/typescript/security-rules/s017-no-sql-injection.js +193 -0
- package/config/typescript/security-rules/s018-positive-input-validation.js +56 -0
- package/config/typescript/security-rules/s019-no-raw-user-input-in-email.js +113 -0
- package/config/typescript/security-rules/s020-no-eval-dynamic-execution.js +89 -0
- package/config/typescript/security-rules/s022-output-encoding.js +78 -0
- package/config/typescript/security-rules/s023-no-json-injection.js +300 -0
- package/config/typescript/security-rules/s025-server-side-input-validation.js +217 -0
- package/config/typescript/security-rules/s026-json-schema-validation.js +68 -0
- package/config/typescript/security-rules/s027-no-hardcoded-secrets.js +80 -0
- package/config/typescript/security-rules/s029-require-csrf-protection.js +79 -0
- package/config/typescript/security-rules/s030-no-directory-browsing.js +78 -0
- package/config/typescript/security-rules/s033-require-samesite-cookie.js +80 -0
- package/config/typescript/security-rules/s034-require-host-cookie-prefix.js +77 -0
- package/config/typescript/security-rules/s035-cookie-specific-path.js +74 -0
- package/config/typescript/security-rules/s036-no-unsafe-file-include.js +68 -0
- package/config/typescript/security-rules/s037-require-anti-cache-headers.js +70 -0
- package/config/typescript/security-rules/s038-no-version-disclosure.js +74 -0
- package/config/typescript/security-rules/s039-no-session-token-in-url.js +63 -0
- package/config/typescript/security-rules/s041-require-session-invalidate-on-logout.js +211 -0
- package/config/typescript/security-rules/s042-require-periodic-reauthentication.js +294 -0
- package/config/typescript/security-rules/s043-terminate-sessions-on-password-change.js +254 -0
- package/config/typescript/security-rules/s044-require-full-session-for-sensitive-operations.js +292 -0
- package/config/typescript/security-rules/s045-anti-automation-controls.js +46 -0
- package/config/typescript/security-rules/s046-secure-notification-on-auth-change.js +44 -0
- package/config/typescript/security-rules/s048-password-credential-recovery.js +54 -0
- package/config/typescript/security-rules/s050-session-token-weak-hash.js +94 -0
- package/config/typescript/security-rules/s052-secure-random-authentication-code.js +66 -0
- package/config/typescript/security-rules/s054-verification-default-account.js +109 -0
- package/config/typescript/security-rules/s057-utc-logging.js +54 -0
- package/config/typescript/security-rules/s058-no-ssrf.js +73 -0
- package/config/typescript/test-s005-working.ts +22 -0
- package/config/typescript/tsconfig.json +29 -0
- package/core/ai-analyzer.js +169 -0
- package/core/analysis-orchestrator.js +705 -0
- package/core/cli-action-handler.js +230 -0
- package/core/cli-program.js +106 -0
- package/core/config-manager.js +396 -0
- package/core/config-merger.js +136 -0
- package/core/config-override-processor.js +74 -0
- package/core/config-preset-resolver.js +65 -0
- package/core/config-source-loader.js +152 -0
- package/core/config-validator.js +126 -0
- package/core/dependency-manager.js +105 -0
- package/core/eslint-engine-service.js +312 -0
- package/core/eslint-instance-manager.js +104 -0
- package/core/eslint-integration-service.js +363 -0
- package/core/git-utils.js +170 -0
- package/core/multi-rule-runner.js +239 -0
- package/core/output-service.js +250 -0
- package/core/report-generator.js +320 -0
- package/core/rule-mapping-service.js +309 -0
- package/core/rule-selection-service.js +121 -0
- package/core/sunlint-engine-service.js +23 -0
- package/core/typescript-analyzer.js +262 -0
- package/core/typescript-engine.js +313 -0
- package/docs/AI.md +163 -0
- package/docs/ARCHITECTURE.md +78 -0
- package/docs/CI-CD-GUIDE.md +315 -0
- package/docs/COMMAND-EXAMPLES.md +256 -0
- package/docs/DEBUG.md +86 -0
- package/docs/DISTRIBUTION.md +153 -0
- package/docs/ESLINT-INTEGRATION-STRATEGY.md +392 -0
- package/docs/ESLINT_INTEGRATION.md +238 -0
- package/docs/FOLDER_STRUCTURE.md +59 -0
- package/docs/HEURISTIC_VS_AI.md +113 -0
- package/docs/README.md +32 -0
- package/docs/RELEASE_GUIDE.md +230 -0
- package/docs/RULE-RESPONSIBILITY-MATRIX.md +204 -0
- package/eslint-integration/.eslintrc.js +98 -0
- package/eslint-integration/cli.js +35 -0
- package/eslint-integration/eslint-plugin-custom/c002-no-duplicate-code.js +204 -0
- package/eslint-integration/eslint-plugin-custom/c003-no-vague-abbreviations.js +246 -0
- package/eslint-integration/eslint-plugin-custom/c006-function-name-verb-noun.js +207 -0
- package/eslint-integration/eslint-plugin-custom/c010-limit-block-nesting.js +90 -0
- package/eslint-integration/eslint-plugin-custom/c013-no-dead-code.js +43 -0
- package/eslint-integration/eslint-plugin-custom/c014-abstract-dependency-preferred.js +38 -0
- package/eslint-integration/eslint-plugin-custom/c017-limit-constructor-logic.js +39 -0
- package/eslint-integration/eslint-plugin-custom/c018-no-generic-throw.js +335 -0
- package/eslint-integration/eslint-plugin-custom/c023-no-duplicate-variable-name-in-scope.js +142 -0
- package/eslint-integration/eslint-plugin-custom/c027-limit-function-nesting.js +50 -0
- package/eslint-integration/eslint-plugin-custom/c029-catch-block-logging.js +80 -0
- package/eslint-integration/eslint-plugin-custom/c030-use-custom-error-classes.js +294 -0
- package/eslint-integration/eslint-plugin-custom/c034-no-implicit-return.js +34 -0
- package/eslint-integration/eslint-plugin-custom/c035-no-empty-catch.js +32 -0
- package/eslint-integration/eslint-plugin-custom/c041-no-config-inline.js +64 -0
- package/eslint-integration/eslint-plugin-custom/c042-boolean-name-prefix.js +406 -0
- package/eslint-integration/eslint-plugin-custom/c043-no-console-or-print.js +300 -0
- package/eslint-integration/eslint-plugin-custom/c047-no-duplicate-retry-logic.js +239 -0
- package/eslint-integration/eslint-plugin-custom/c048-no-var-declaration.js +31 -0
- package/eslint-integration/eslint-plugin-custom/c076-one-assert-per-test.js +184 -0
- package/eslint-integration/eslint-plugin-custom/index.js +155 -0
- package/eslint-integration/eslint-plugin-custom/package.json +13 -0
- package/eslint-integration/eslint-plugin-custom/package.json.bak +9 -0
- package/eslint-integration/eslint-plugin-custom/s003-no-unvalidated-redirect.js +86 -0
- package/eslint-integration/eslint-plugin-custom/s005-no-origin-auth.js +95 -0
- package/eslint-integration/eslint-plugin-custom/s006-activation-recovery-secret-not-plaintext.js +69 -0
- package/eslint-integration/eslint-plugin-custom/s008-crypto-agility.js +62 -0
- package/eslint-integration/eslint-plugin-custom/s009-no-insecure-crypto.js +103 -0
- package/eslint-integration/eslint-plugin-custom/s010-no-insecure-random-in-sensitive-context.js +123 -0
- package/eslint-integration/eslint-plugin-custom/s011-no-insecure-uuid.js +66 -0
- package/eslint-integration/eslint-plugin-custom/s012-hardcode-secret.js +71 -0
- package/eslint-integration/eslint-plugin-custom/s014-insecure-tls-version.js +50 -0
- package/eslint-integration/eslint-plugin-custom/s015-insecure-tls-certificate.js +43 -0
- package/eslint-integration/eslint-plugin-custom/s016-sensitive-query-parameter.js +59 -0
- package/eslint-integration/eslint-plugin-custom/s017-no-sql-injection.js +193 -0
- package/eslint-integration/eslint-plugin-custom/s018-positive-input-validation.js +56 -0
- package/eslint-integration/eslint-plugin-custom/s019-no-raw-user-input-in-email.js +113 -0
- package/eslint-integration/eslint-plugin-custom/s020-no-eval-dynamic-execution.js +89 -0
- package/eslint-integration/eslint-plugin-custom/s022-output-encoding.js +78 -0
- package/eslint-integration/eslint-plugin-custom/s023-no-json-injection.js +300 -0
- package/eslint-integration/eslint-plugin-custom/s025-server-side-input-validation.js +217 -0
- package/eslint-integration/eslint-plugin-custom/s026-json-schema-validation.js +68 -0
- package/eslint-integration/eslint-plugin-custom/s027-no-hardcoded-secrets.js +80 -0
- package/eslint-integration/eslint-plugin-custom/s029-require-csrf-protection.js +79 -0
- package/eslint-integration/eslint-plugin-custom/s030-no-directory-browsing.js +78 -0
- package/eslint-integration/eslint-plugin-custom/s033-require-samesite-cookie.js +80 -0
- package/eslint-integration/eslint-plugin-custom/s034-require-host-cookie-prefix.js +77 -0
- package/eslint-integration/eslint-plugin-custom/s035-cookie-specific-path.js +74 -0
- package/eslint-integration/eslint-plugin-custom/s036-no-unsafe-file-include.js +68 -0
- package/eslint-integration/eslint-plugin-custom/s037-require-anti-cache-headers.js +70 -0
- package/eslint-integration/eslint-plugin-custom/s038-no-version-disclosure.js +74 -0
- package/eslint-integration/eslint-plugin-custom/s039-no-session-token-in-url.js +63 -0
- package/eslint-integration/eslint-plugin-custom/s041-require-session-invalidate-on-logout.js +211 -0
- package/eslint-integration/eslint-plugin-custom/s042-require-periodic-reauthentication.js +294 -0
- package/eslint-integration/eslint-plugin-custom/s043-terminate-sessions-on-password-change.js +254 -0
- package/eslint-integration/eslint-plugin-custom/s044-require-full-session-for-sensitive-operations.js +292 -0
- package/eslint-integration/eslint-plugin-custom/s045-anti-automation-controls.js +46 -0
- package/eslint-integration/eslint-plugin-custom/s046-secure-notification-on-auth-change.js +44 -0
- package/eslint-integration/eslint-plugin-custom/s047-secure-random-passwords.js +108 -0
- package/eslint-integration/eslint-plugin-custom/s048-password-credential-recovery.js +54 -0
- package/eslint-integration/eslint-plugin-custom/s050-session-token-weak-hash.js +94 -0
- package/eslint-integration/eslint-plugin-custom/s052-secure-random-authentication-code.js +66 -0
- package/eslint-integration/eslint-plugin-custom/s054-verification-default-account.js +109 -0
- package/eslint-integration/eslint-plugin-custom/s055-verification-rest-check-the-incoming-content-type.js +143 -0
- package/eslint-integration/eslint-plugin-custom/s057-utc-logging.js +54 -0
- package/eslint-integration/eslint-plugin-custom/s058-no-ssrf.js +73 -0
- package/eslint-integration/eslint-plugin-custom/t002-interface-prefix-i.js +42 -0
- package/eslint-integration/eslint-plugin-custom/t003-ts-ignore-reason.js +48 -0
- package/eslint-integration/eslint-plugin-custom/t004-interface-public-only.js +160 -0
- package/eslint-integration/eslint-plugin-custom/t007-no-fn-in-constructor.js +52 -0
- package/eslint-integration/eslint-plugin-custom/t011-no-real-time-dependency.js +175 -0
- package/eslint-integration/eslint-plugin-custom/t019-no-empty-type.js +95 -0
- package/eslint-integration/eslint-plugin-custom/t025-no-nested-union-tuple.js +48 -0
- package/eslint-integration/eslint-plugin-custom/t026-limit-nested-generics.js +377 -0
- package/eslint-integration/eslint.config.js +125 -0
- package/eslint-integration/eslint.config.simple.js +24 -0
- package/eslint-integration/node_modules/eslint-plugin-custom/package.json +0 -0
- package/eslint-integration/package.json +23 -0
- package/eslint-integration/sample.ts +53 -0
- package/eslint-integration/test-s003.js +5 -0
- package/eslint-integration/tsconfig.json +27 -0
- package/examples/.github/workflows/code-quality.yml +111 -0
- package/examples/.sunlint.json +42 -0
- package/examples/README.md +47 -0
- package/examples/package.json +33 -0
- package/package.json +100 -0
- package/rules/C006_function_naming/analyzer.js +338 -0
- package/rules/C006_function_naming/config.json +86 -0
- package/rules/C019_log_level_usage/analyzer.js +359 -0
- package/rules/C019_log_level_usage/config.json +121 -0
- package/rules/C029_catch_block_logging/analyzer.js +339 -0
- package/rules/C029_catch_block_logging/config.json +59 -0
- package/rules/C031_validation_separation/README.md +72 -0
- package/rules/C031_validation_separation/analyzer.js +186 -0
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ESLint rule for: C042 – Boolean variable names should start with `is`, `has`, or `should`
|
|
3
|
+
* Rule ID: custom/c042
|
|
4
|
+
* Purpose: Ensure boolean variables have clear naming conventions
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const c042Rule = {
|
|
8
|
+
meta: {
|
|
9
|
+
type: "suggestion",
|
|
10
|
+
docs: {
|
|
11
|
+
description: "Boolean variable names should start with `is`, `has`, or `should`",
|
|
12
|
+
recommended: true
|
|
13
|
+
},
|
|
14
|
+
schema: [
|
|
15
|
+
{
|
|
16
|
+
type: "object",
|
|
17
|
+
properties: {
|
|
18
|
+
allowedPrefixes: {
|
|
19
|
+
type: "array",
|
|
20
|
+
items: { type: "string" },
|
|
21
|
+
description: "Additional boolean prefixes to allow (default: is, has, should, can, will, must, may)"
|
|
22
|
+
},
|
|
23
|
+
strictMode: {
|
|
24
|
+
type: "boolean",
|
|
25
|
+
description: "Whether to enforce strict boolean prefixes only (default: false)"
|
|
26
|
+
},
|
|
27
|
+
ignoredNames: {
|
|
28
|
+
type: "array",
|
|
29
|
+
items: { type: "string" },
|
|
30
|
+
description: "Variable names to ignore (e.g., common patterns like 'flag', 'enabled')"
|
|
31
|
+
},
|
|
32
|
+
checkReturnTypes: {
|
|
33
|
+
type: "boolean",
|
|
34
|
+
description: "Whether to check function return types for boolean naming (default: true)"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
additionalProperties: false
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
messages: {
|
|
41
|
+
booleanNaming: "Boolean variable '{{name}}' should start with a descriptive prefix like 'is', 'has', or 'should'. Consider: {{suggestions}}.",
|
|
42
|
+
booleanFunction: "Function '{{name}}' returns boolean but name doesn't follow boolean naming convention. Consider: {{suggestions}}.",
|
|
43
|
+
improperPrefix: "Variable '{{name}}' uses prefix '{{prefix}}' but is not a boolean type.",
|
|
44
|
+
strictPrefix: "Boolean variable '{{name}}' must use one of the allowed prefixes: {{allowedPrefixes}}."
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
|
|
48
|
+
create(context) {
|
|
49
|
+
const options = context.options[0] || {};
|
|
50
|
+
|
|
51
|
+
// Default boolean prefixes
|
|
52
|
+
const defaultPrefixes = ['is', 'has', 'should', 'can', 'will', 'must', 'may', 'was', 'were'];
|
|
53
|
+
const allowedPrefixes = new Set([
|
|
54
|
+
...defaultPrefixes,
|
|
55
|
+
...(options.allowedPrefixes || [])
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
const strictMode = options.strictMode || false;
|
|
59
|
+
const ignoredNames = new Set(options.ignoredNames || ['flag', 'enabled', 'disabled', 'active', 'valid', 'ok']);
|
|
60
|
+
const checkReturnTypes = options.checkReturnTypes !== false;
|
|
61
|
+
|
|
62
|
+
// Common boolean value patterns
|
|
63
|
+
const booleanValuePatterns = [
|
|
64
|
+
/^(true|false)$/,
|
|
65
|
+
/^!(.*)/, // Negation
|
|
66
|
+
/^.*\s*(===|!==|==|!=|<|>|<=|>=)\s*.*/, // Comparison
|
|
67
|
+
/^.*\s*(&&|\|\|)\s*.*/, // Logical operators
|
|
68
|
+
];
|
|
69
|
+
|
|
70
|
+
// Function call patterns that typically return boolean
|
|
71
|
+
const booleanFunctionPatterns = [
|
|
72
|
+
/^(test|check|validate|verify|confirm|ensure).*$/i,
|
|
73
|
+
/^.*\.(test|includes|startsWith|endsWith|match)$/i,
|
|
74
|
+
/^(Array\.isArray|Number\.isNaN|Number\.isFinite)$/i
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
function startsWithBooleanPrefix(name) {
|
|
78
|
+
const lowerName = name.toLowerCase();
|
|
79
|
+
return Array.from(allowedPrefixes).some(prefix =>
|
|
80
|
+
lowerName.startsWith(prefix.toLowerCase())
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function generateSuggestions(name) {
|
|
85
|
+
const suggestions = [
|
|
86
|
+
`is${name.charAt(0).toUpperCase() + name.slice(1)}`,
|
|
87
|
+
`has${name.charAt(0).toUpperCase() + name.slice(1)}`,
|
|
88
|
+
`should${name.charAt(0).toUpperCase() + name.slice(1)}`
|
|
89
|
+
];
|
|
90
|
+
return suggestions.join(', ');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function isBooleanLiteral(node) {
|
|
94
|
+
if (!node) return false;
|
|
95
|
+
|
|
96
|
+
if (node.type === 'Literal') {
|
|
97
|
+
return typeof node.value === 'boolean';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (node.type === 'UnaryExpression' && node.operator === '!') {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (node.type === 'BinaryExpression') {
|
|
105
|
+
const comparisonOps = ['===', '!==', '==', '!=', '<', '>', '<=', '>='];
|
|
106
|
+
const logicalOps = ['&&', '||'];
|
|
107
|
+
return comparisonOps.includes(node.operator) || logicalOps.includes(node.operator);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (node.type === 'LogicalExpression') {
|
|
111
|
+
return ['&&', '||'].includes(node.operator);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (node.type === 'CallExpression') {
|
|
115
|
+
try {
|
|
116
|
+
const source = context.getSourceCode();
|
|
117
|
+
const callText = source.getText(node);
|
|
118
|
+
return booleanFunctionPatterns.some(pattern => pattern.test(callText));
|
|
119
|
+
} catch (error) {
|
|
120
|
+
// If we can't get source text, assume it's not boolean
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function isBooleanTypeAnnotation(node) {
|
|
129
|
+
// TypeScript type annotations
|
|
130
|
+
if (node.typeAnnotation && node.typeAnnotation.typeAnnotation) {
|
|
131
|
+
const typeNode = node.typeAnnotation.typeAnnotation;
|
|
132
|
+
return typeNode.type === 'TSBooleanKeyword';
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function getFunctionReturnType(node) {
|
|
138
|
+
// Check TypeScript return type annotation
|
|
139
|
+
if (node.returnType && node.returnType.typeAnnotation) {
|
|
140
|
+
return node.returnType.typeAnnotation.type === 'TSBooleanKeyword';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Analyze return statements with safe traversal
|
|
144
|
+
const returnStatements = [];
|
|
145
|
+
const visitedNodes = new WeakSet();
|
|
146
|
+
|
|
147
|
+
function findReturnStatements(astNode, depth = 0) {
|
|
148
|
+
// Prevent infinite recursion
|
|
149
|
+
if (depth > 50 || !astNode || typeof astNode !== 'object' || visitedNodes.has(astNode)) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
visitedNodes.add(astNode);
|
|
154
|
+
|
|
155
|
+
if (astNode.type === 'ReturnStatement') {
|
|
156
|
+
returnStatements.push(astNode);
|
|
157
|
+
return; // Don't traverse further into return statements
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Stop traversing into nested functions to avoid checking their returns
|
|
161
|
+
if (astNode.type === 'FunctionDeclaration' ||
|
|
162
|
+
astNode.type === 'FunctionExpression' ||
|
|
163
|
+
astNode.type === 'ArrowFunctionExpression') {
|
|
164
|
+
if (astNode !== node) { // Don't skip the original node
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Safely traverse specific node properties that might contain return statements
|
|
170
|
+
const propertiesToCheck = ['body', 'consequent', 'alternate', 'cases', 'statements'];
|
|
171
|
+
|
|
172
|
+
for (const prop of propertiesToCheck) {
|
|
173
|
+
if (astNode[prop]) {
|
|
174
|
+
if (Array.isArray(astNode[prop])) {
|
|
175
|
+
astNode[prop].forEach(child => {
|
|
176
|
+
if (child && typeof child === 'object') {
|
|
177
|
+
findReturnStatements(child, depth + 1);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
} else if (typeof astNode[prop] === 'object') {
|
|
181
|
+
findReturnStatements(astNode[prop], depth + 1);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (node.body) {
|
|
188
|
+
findReturnStatements(node.body);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Check if all return statements return boolean values
|
|
192
|
+
if (returnStatements.length > 0) {
|
|
193
|
+
return returnStatements.every(stmt =>
|
|
194
|
+
stmt.argument && isBooleanLiteral(stmt.argument)
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function checkVariableName(node, name, init = null) {
|
|
202
|
+
if (!name) return;
|
|
203
|
+
|
|
204
|
+
// Skip ignored names
|
|
205
|
+
if (ignoredNames.has(name.toLowerCase())) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Skip very short names (likely not descriptive anyway)
|
|
210
|
+
if (name.length <= 2) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const hasBooleanPrefix = startsWithBooleanPrefix(name);
|
|
215
|
+
const isBooleanType = isBooleanTypeAnnotation(node);
|
|
216
|
+
|
|
217
|
+
let isBooleanValue = false;
|
|
218
|
+
try {
|
|
219
|
+
isBooleanValue = init && isBooleanLiteral(init);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
// Skip boolean value check if we can't determine it safely
|
|
222
|
+
isBooleanValue = false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Case 1: Variable has boolean type annotation or boolean value
|
|
226
|
+
if (isBooleanType || isBooleanValue) {
|
|
227
|
+
if (!hasBooleanPrefix) {
|
|
228
|
+
if (strictMode) {
|
|
229
|
+
context.report({
|
|
230
|
+
node,
|
|
231
|
+
messageId: "strictPrefix",
|
|
232
|
+
data: {
|
|
233
|
+
name,
|
|
234
|
+
allowedPrefixes: Array.from(allowedPrefixes).join(', ')
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
} else {
|
|
238
|
+
context.report({
|
|
239
|
+
node,
|
|
240
|
+
messageId: "booleanNaming",
|
|
241
|
+
data: {
|
|
242
|
+
name,
|
|
243
|
+
suggestions: generateSuggestions(name)
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Case 2: Variable has boolean prefix but not boolean type/value
|
|
251
|
+
else if (hasBooleanPrefix && !isBooleanType && !isBooleanValue) {
|
|
252
|
+
// Only warn if we can determine it's definitely not boolean
|
|
253
|
+
if (init && init.type === 'Literal' && typeof init.value !== 'boolean') {
|
|
254
|
+
const prefix = Array.from(allowedPrefixes).find(p =>
|
|
255
|
+
name.toLowerCase().startsWith(p.toLowerCase())
|
|
256
|
+
);
|
|
257
|
+
context.report({
|
|
258
|
+
node,
|
|
259
|
+
messageId: "improperPrefix",
|
|
260
|
+
data: {
|
|
261
|
+
name,
|
|
262
|
+
prefix
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function checkFunctionName(node, name) {
|
|
270
|
+
if (!checkReturnTypes || !name) return;
|
|
271
|
+
|
|
272
|
+
// Skip ignored names
|
|
273
|
+
if (ignoredNames.has(name.toLowerCase())) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Skip very short names and common patterns
|
|
278
|
+
if (name.length <= 3 || name === 'main' || name === 'init' || name === 'setup') {
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Skip constructor functions
|
|
283
|
+
if (name[0] === name[0].toUpperCase()) {
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Skip event handlers and common callback patterns
|
|
288
|
+
if (name.startsWith('handle') || name.startsWith('on') || name.includes('Handler') || name.includes('Callback')) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const hasBooleanPrefix = startsWithBooleanPrefix(name);
|
|
293
|
+
|
|
294
|
+
try {
|
|
295
|
+
const returnsBooleanType = getFunctionReturnType(node);
|
|
296
|
+
|
|
297
|
+
// Function returns boolean but doesn't have boolean name
|
|
298
|
+
if (returnsBooleanType && !hasBooleanPrefix) {
|
|
299
|
+
context.report({
|
|
300
|
+
node: node.id || node,
|
|
301
|
+
messageId: "booleanFunction",
|
|
302
|
+
data: {
|
|
303
|
+
name,
|
|
304
|
+
suggestions: generateSuggestions(name)
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
} catch (error) {
|
|
309
|
+
// Skip this check if we encounter an error to prevent crashes
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
VariableDeclarator(node) {
|
|
316
|
+
if (node.id && node.id.type === 'Identifier') {
|
|
317
|
+
checkVariableName(node.id, node.id.name, node.init);
|
|
318
|
+
} else if (node.id && node.id.type === 'ObjectPattern') {
|
|
319
|
+
// Handle destructuring: const {isActive, hasPermission} = obj;
|
|
320
|
+
node.id.properties.forEach(prop => {
|
|
321
|
+
if (prop.type === 'Property' && prop.value && prop.value.type === 'Identifier') {
|
|
322
|
+
checkVariableName(prop.value, prop.value.name);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
} else if (node.id && node.id.type === 'ArrayPattern') {
|
|
326
|
+
// Handle array destructuring: const [isEnabled, hasAccess] = flags;
|
|
327
|
+
node.id.elements.forEach(element => {
|
|
328
|
+
if (element && element.type === 'Identifier') {
|
|
329
|
+
checkVariableName(element, element.name);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
FunctionDeclaration(node) {
|
|
336
|
+
if (node.id && node.id.name) {
|
|
337
|
+
checkFunctionName(node, node.id.name);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Check parameters
|
|
341
|
+
if (node.params) {
|
|
342
|
+
node.params.forEach(param => {
|
|
343
|
+
if (param.type === 'Identifier') {
|
|
344
|
+
checkVariableName(param, param.name);
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
|
|
350
|
+
FunctionExpression(node) {
|
|
351
|
+
// Check named function expressions
|
|
352
|
+
if (node.id && node.id.name) {
|
|
353
|
+
checkFunctionName(node, node.id.name);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Check parameters
|
|
357
|
+
if (node.params) {
|
|
358
|
+
node.params.forEach(param => {
|
|
359
|
+
if (param.type === 'Identifier') {
|
|
360
|
+
checkVariableName(param, param.name);
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
},
|
|
365
|
+
|
|
366
|
+
ArrowFunctionExpression(node) {
|
|
367
|
+
// For arrow functions assigned to variables
|
|
368
|
+
if (node.parent && node.parent.type === 'VariableDeclarator' && node.parent.id) {
|
|
369
|
+
checkFunctionName(node, node.parent.id.name);
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
// Check parameters
|
|
373
|
+
if (node.params) {
|
|
374
|
+
node.params.forEach(param => {
|
|
375
|
+
if (param.type === 'Identifier') {
|
|
376
|
+
checkVariableName(param, param.name);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
|
|
382
|
+
MethodDefinition(node) {
|
|
383
|
+
// Check class methods
|
|
384
|
+
if (node.key && node.key.name && node.kind === 'method') {
|
|
385
|
+
checkFunctionName(node.value, node.key.name);
|
|
386
|
+
}
|
|
387
|
+
},
|
|
388
|
+
|
|
389
|
+
Property(node) {
|
|
390
|
+
// Check object method properties
|
|
391
|
+
if (node.method && node.key && node.key.name) {
|
|
392
|
+
checkFunctionName(node.value, node.key.name);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Check boolean properties
|
|
396
|
+
if (!node.method && node.key && node.key.name && node.value) {
|
|
397
|
+
if (isBooleanLiteral(node.value)) {
|
|
398
|
+
checkVariableName(node.key, node.key.name, node.value);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
module.exports = c042Rule;
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ESLint rule for: C043 – Do not use `console.log` or `print` in production code
|
|
3
|
+
* Rule ID: custom/c043
|
|
4
|
+
* Purpose: Prevent usage of console logging in production code to maintain clean logs and security
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const c045Rule = {
|
|
8
|
+
meta: {
|
|
9
|
+
type: "suggestion",
|
|
10
|
+
docs: {
|
|
11
|
+
description: "Do not use `console.log` or `print` in production code",
|
|
12
|
+
recommended: false
|
|
13
|
+
},
|
|
14
|
+
schema: [],
|
|
15
|
+
messages: {
|
|
16
|
+
noConsole: "Do not use console.log in production code. Use proper logging instead.",
|
|
17
|
+
noPrint: "Do not use print in production code. Use proper logging instead."
|
|
18
|
+
},
|
|
19
|
+
fixable: "code"
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
create(context) {
|
|
23
|
+
const options = context.options[0] || {};
|
|
24
|
+
|
|
25
|
+
// Default allowed console methods (typically for errors/warnings)
|
|
26
|
+
const allowedMethods = new Set(options.allowedMethods || ['error', 'warn']);
|
|
27
|
+
|
|
28
|
+
const allowInDevelopment = options.allowInDevelopment !== false;
|
|
29
|
+
const allowInTests = options.allowInTests !== false;
|
|
30
|
+
|
|
31
|
+
// Default patterns for test files
|
|
32
|
+
const testFilePatterns = options.testFilePatterns || [
|
|
33
|
+
'.test.', '.spec.', '__tests__', '/test/', '/tests/', '.test.ts', '.test.js',
|
|
34
|
+
'.spec.ts', '.spec.js', 'test.tsx', 'spec.tsx'
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
// Default patterns for development files
|
|
38
|
+
const developmentPatterns = options.developmentPatterns || [
|
|
39
|
+
'.dev.', '.development.', '.debug.', '/dev/', '/development/'
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
// Development environment flags
|
|
43
|
+
const developmentFlags = new Set(options.developmentFlags || [
|
|
44
|
+
'__DEV__', 'DEBUG', 'process.env.NODE_ENV', 'process.env.ENVIRONMENT',
|
|
45
|
+
'process.env.ENABLE_LOGGING', 'FEATURES.debug', 'BUILD_TYPE'
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
// Other forbidden objects/functions
|
|
49
|
+
const allowedObjects = options.allowedObjects || ['print', 'alert', 'confirm', 'prompt'];
|
|
50
|
+
const forbiddenObjects = new Set(['console', ...allowedObjects]);
|
|
51
|
+
|
|
52
|
+
// Common console methods to check
|
|
53
|
+
const consoleMethods = new Set([
|
|
54
|
+
'log', 'info', 'debug', 'trace', 'dir', 'dirxml', 'table',
|
|
55
|
+
'count', 'countReset', 'time', 'timeEnd', 'timeLog',
|
|
56
|
+
'assert', 'clear', 'group', 'groupCollapsed', 'groupEnd',
|
|
57
|
+
'profile', 'profileEnd', 'timeStamp'
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
// Get current file information
|
|
61
|
+
const filename = context.getFilename();
|
|
62
|
+
const sourceCode = context.getSourceCode();
|
|
63
|
+
|
|
64
|
+
function isTestFile() {
|
|
65
|
+
return testFilePatterns.some(pattern => filename.includes(pattern));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function isDevelopmentFile() {
|
|
69
|
+
return developmentPatterns.some(pattern => filename.includes(pattern));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Recursively check all sub-expressions for development flags
|
|
73
|
+
function containsDevelopmentFlag(node) {
|
|
74
|
+
if (!node) return false;
|
|
75
|
+
// Direct flag usage (__DEV__, DEBUG)
|
|
76
|
+
if (node.type === 'Identifier' && developmentFlags.has(node.name)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
// process.env.NODE_ENV === 'development'
|
|
80
|
+
if (node.type === 'BinaryExpression' && node.operator === '===' && node.right.value === 'development') {
|
|
81
|
+
const left = node.left;
|
|
82
|
+
if (left.type === 'MemberExpression' &&
|
|
83
|
+
left.object.type === 'MemberExpression' &&
|
|
84
|
+
left.object.object.name === 'process' &&
|
|
85
|
+
left.object.property.name === 'env') {
|
|
86
|
+
const envVar = left.property.name;
|
|
87
|
+
if (envVar === 'NODE_ENV' || developmentFlags.has(`process.env.${envVar}`)) {
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// process.env.<VAR> === 'true'
|
|
93
|
+
if (node.type === 'BinaryExpression' && node.operator === '===' && node.right.value === 'true') {
|
|
94
|
+
const left = node.left;
|
|
95
|
+
if (left.type === 'MemberExpression' &&
|
|
96
|
+
left.object.type === 'MemberExpression' &&
|
|
97
|
+
left.object.object.name === 'process' &&
|
|
98
|
+
left.object.property.name === 'env') {
|
|
99
|
+
const envVar = left.property.name;
|
|
100
|
+
if (developmentFlags.has(`process.env.${envVar}`)) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Feature flags (FEATURES.debug, etc.)
|
|
106
|
+
if (node.type === 'MemberExpression' &&
|
|
107
|
+
node.object.type === 'Identifier' &&
|
|
108
|
+
node.property.type === 'Identifier') {
|
|
109
|
+
const objectName = node.object.name;
|
|
110
|
+
const propertyName = node.property.name;
|
|
111
|
+
if (developmentFlags.has(`${objectName}.${propertyName}`)) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Logical expressions (||, &&)
|
|
116
|
+
if (node.type === 'LogicalExpression') {
|
|
117
|
+
return containsDevelopmentFlag(node.left) || containsDevelopmentFlag(node.right);
|
|
118
|
+
}
|
|
119
|
+
// Nested binary/logical expressions
|
|
120
|
+
if (node.left && containsDevelopmentFlag(node.left)) return true;
|
|
121
|
+
if (node.right && containsDevelopmentFlag(node.right)) return true;
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Walk up the AST to see if inside any IfStatement with a dev/debug flag
|
|
126
|
+
function isInDevelopmentContext(node) {
|
|
127
|
+
let current = node;
|
|
128
|
+
while (current && current.parent) {
|
|
129
|
+
if (current.parent.type === 'IfStatement') {
|
|
130
|
+
const test = current.parent.test;
|
|
131
|
+
if (containsDevelopmentFlag(test)) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
current = current.parent;
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function shouldAllowCall(node) {
|
|
141
|
+
if (allowInTests && isTestFile()) {
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
if (allowInDevelopment && (isDevelopmentFile() || isInDevelopmentContext(node))) {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function checkConsoleCall(node) {
|
|
151
|
+
if (!node.callee) return;
|
|
152
|
+
|
|
153
|
+
// Check for console.method() calls
|
|
154
|
+
if (node.callee.type === 'MemberExpression') {
|
|
155
|
+
const object = node.callee.object;
|
|
156
|
+
const property = node.callee.property;
|
|
157
|
+
|
|
158
|
+
if (object && property &&
|
|
159
|
+
object.type === 'Identifier' &&
|
|
160
|
+
property.type === 'Identifier') {
|
|
161
|
+
|
|
162
|
+
const objectName = object.name;
|
|
163
|
+
const methodName = property.name;
|
|
164
|
+
|
|
165
|
+
// Check console calls
|
|
166
|
+
if (objectName === 'console') {
|
|
167
|
+
// Skip if method is explicitly allowed
|
|
168
|
+
if (allowedMethods.has(methodName)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Skip if file context allows it
|
|
173
|
+
if (shouldAllowCall(node)) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Report console violation
|
|
178
|
+
if (consoleMethods.has(methodName) || methodName === 'log') {
|
|
179
|
+
context.report({
|
|
180
|
+
node,
|
|
181
|
+
messageId: methodName === 'log' ? "noConsole" : "noConsole",
|
|
182
|
+
data: { method: methodName },
|
|
183
|
+
fix(fixer) {
|
|
184
|
+
// Offer to remove the entire statement
|
|
185
|
+
const statement = findStatementNode(node);
|
|
186
|
+
if (statement) {
|
|
187
|
+
return fixer.remove(statement);
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Check other forbidden objects (print, alert, etc.)
|
|
196
|
+
else if (forbiddenObjects.has(objectName) && objectName !== 'console') {
|
|
197
|
+
if (shouldAllowCall(node)) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
context.report({
|
|
202
|
+
node,
|
|
203
|
+
messageId: objectName === 'alert' ? "noConsole" : "noPrint",
|
|
204
|
+
data: { object: objectName, method: methodName }
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Check for direct function calls (print(), alert(), etc.)
|
|
211
|
+
else if (node.callee.type === 'Identifier') {
|
|
212
|
+
const functionName = node.callee.name;
|
|
213
|
+
|
|
214
|
+
// Check for standalone forbidden functions
|
|
215
|
+
if (['print', 'alert', 'confirm', 'prompt'].includes(functionName)) {
|
|
216
|
+
if (shouldAllowCall(node)) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
context.report({
|
|
221
|
+
node,
|
|
222
|
+
messageId: functionName === 'alert' ? "noConsole" : "noPrint",
|
|
223
|
+
data: { method: functionName }
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function findStatementNode(node) {
|
|
230
|
+
let current = node;
|
|
231
|
+
while (current && current.parent) {
|
|
232
|
+
if (current.parent.type === 'ExpressionStatement') {
|
|
233
|
+
return current.parent;
|
|
234
|
+
}
|
|
235
|
+
if (current.parent.type === 'Program' ||
|
|
236
|
+
current.parent.type === 'BlockStatement') {
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
current = current.parent;
|
|
240
|
+
}
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function checkTemplateLiteral(node) {
|
|
245
|
+
// Check for console calls in template literals (less common but possible)
|
|
246
|
+
if (node.expressions) {
|
|
247
|
+
node.expressions.forEach(expr => {
|
|
248
|
+
if (expr.type === 'CallExpression') {
|
|
249
|
+
checkConsoleCall(expr);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function checkDebugger(node) {
|
|
256
|
+
// Also flag debugger statements
|
|
257
|
+
if (!shouldAllowCall(node)) {
|
|
258
|
+
context.report({
|
|
259
|
+
node,
|
|
260
|
+
message: "Debugger statements should not be used in production code.",
|
|
261
|
+
fix(fixer) {
|
|
262
|
+
const statement = findStatementNode(node);
|
|
263
|
+
if (statement) {
|
|
264
|
+
return fixer.remove(statement);
|
|
265
|
+
}
|
|
266
|
+
return fixer.remove(node);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return {
|
|
273
|
+
CallExpression: checkConsoleCall,
|
|
274
|
+
TemplateLiteral: checkTemplateLiteral,
|
|
275
|
+
DebuggerStatement: checkDebugger,
|
|
276
|
+
|
|
277
|
+
// Also check for console calls in other contexts
|
|
278
|
+
MemberExpression(node) {
|
|
279
|
+
// Flag console object access even without calls
|
|
280
|
+
if (node.object &&
|
|
281
|
+
node.object.type === 'Identifier' &&
|
|
282
|
+
node.object.name === 'console' &&
|
|
283
|
+
node.property &&
|
|
284
|
+
node.property.type === 'Identifier') {
|
|
285
|
+
|
|
286
|
+
const methodName = node.property.name;
|
|
287
|
+
if (!allowedMethods.has(methodName) && !shouldAllowCall(node)) {
|
|
288
|
+
context.report({
|
|
289
|
+
node,
|
|
290
|
+
messageId: "noConsole",
|
|
291
|
+
data: { method: methodName }
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
module.exports = c045Rule;
|