@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,111 @@
|
|
|
1
|
+
name: Code Quality & Security Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main, develop]
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
# Parallel execution for faster CI
|
|
11
|
+
eslint:
|
|
12
|
+
name: ESLint Check
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
with:
|
|
17
|
+
fetch-depth: 0
|
|
18
|
+
|
|
19
|
+
- name: Setup Node.js
|
|
20
|
+
uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: '18'
|
|
23
|
+
cache: 'npm'
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: npm ci
|
|
27
|
+
|
|
28
|
+
- name: Run ESLint on changed files
|
|
29
|
+
if: github.event_name == 'pull_request'
|
|
30
|
+
run: |
|
|
31
|
+
CHANGED_FILES=$(git diff --name-only --diff-filter=AM origin/${{ github.base_ref }} | grep -E '\.(js|ts|tsx)$' | tr '\n' ' ')
|
|
32
|
+
if [ ! -z "$CHANGED_FILES" ]; then
|
|
33
|
+
npx eslint $CHANGED_FILES
|
|
34
|
+
else
|
|
35
|
+
echo "No JS/TS files changed"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
- name: Run ESLint full scan
|
|
39
|
+
if: github.event_name == 'push'
|
|
40
|
+
run: npm run lint:eslint
|
|
41
|
+
|
|
42
|
+
sunlint:
|
|
43
|
+
name: SunLint Security & Quality Check
|
|
44
|
+
runs-on: ubuntu-latest
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v4
|
|
47
|
+
with:
|
|
48
|
+
fetch-depth: 0
|
|
49
|
+
|
|
50
|
+
- name: Setup Node.js
|
|
51
|
+
uses: actions/setup-node@v4
|
|
52
|
+
with:
|
|
53
|
+
node-version: '18'
|
|
54
|
+
cache: 'npm'
|
|
55
|
+
|
|
56
|
+
- name: Install dependencies
|
|
57
|
+
run: npm ci
|
|
58
|
+
|
|
59
|
+
- name: Install SunLint
|
|
60
|
+
run: npm install -g @sun/sunlint
|
|
61
|
+
|
|
62
|
+
- name: Run SunLint on changed files (PR)
|
|
63
|
+
if: github.event_name == 'pull_request'
|
|
64
|
+
run: |
|
|
65
|
+
sunlint --all --changed-files --diff-base=origin/${{ github.base_ref }} \
|
|
66
|
+
--format=github --fail-on-new-violations
|
|
67
|
+
|
|
68
|
+
- name: Run SunLint full scan (Main)
|
|
69
|
+
if: github.event_name == 'push'
|
|
70
|
+
run: |
|
|
71
|
+
sunlint --all --input=. --format=json \
|
|
72
|
+
--save-baseline=sunlint-baseline.json --output=sunlint-report.json
|
|
73
|
+
|
|
74
|
+
- name: Upload SunLint Baseline
|
|
75
|
+
if: github.event_name == 'push'
|
|
76
|
+
uses: actions/upload-artifact@v4
|
|
77
|
+
with:
|
|
78
|
+
name: sunlint-baseline
|
|
79
|
+
path: sunlint-baseline.json
|
|
80
|
+
|
|
81
|
+
- name: Upload SunLint Report
|
|
82
|
+
if: always()
|
|
83
|
+
uses: actions/upload-artifact@v4
|
|
84
|
+
with:
|
|
85
|
+
name: sunlint-report
|
|
86
|
+
path: sunlint-report.json
|
|
87
|
+
|
|
88
|
+
# Combined summary job
|
|
89
|
+
quality-gate:
|
|
90
|
+
name: Quality Gate
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
needs: [eslint, sunlint]
|
|
93
|
+
if: always()
|
|
94
|
+
steps:
|
|
95
|
+
- name: Check ESLint Status
|
|
96
|
+
if: needs.eslint.result != 'success'
|
|
97
|
+
run: |
|
|
98
|
+
echo "❌ ESLint check failed"
|
|
99
|
+
exit 1
|
|
100
|
+
|
|
101
|
+
- name: Check SunLint Status
|
|
102
|
+
if: needs.sunlint.result != 'success'
|
|
103
|
+
run: |
|
|
104
|
+
echo "❌ SunLint check failed"
|
|
105
|
+
exit 1
|
|
106
|
+
|
|
107
|
+
- name: Quality Gate Passed
|
|
108
|
+
run: |
|
|
109
|
+
echo "✅ All quality checks passed!"
|
|
110
|
+
echo "📊 ESLint: ${{ needs.eslint.result }}"
|
|
111
|
+
echo "🔒 SunLint: ${{ needs.sunlint.result }}"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": ["@sun/sunlint/recommended"],
|
|
3
|
+
"rules": {
|
|
4
|
+
// SunLint Security Rules
|
|
5
|
+
"S003": "warn", // URL Redirect Validation
|
|
6
|
+
"S047": "warn", // Secure Random Password Generation
|
|
7
|
+
"S055": "warn", // REST Content-Type Verification
|
|
8
|
+
"C006": "error", // Function Naming Convention
|
|
9
|
+
"C019": "error" // Log Level Usage
|
|
10
|
+
},
|
|
11
|
+
"eslint": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"runBefore": true,
|
|
14
|
+
"configPath": ".eslintrc.js",
|
|
15
|
+
"rules": {
|
|
16
|
+
// Keep existing ESLint rules
|
|
17
|
+
"no-unused-vars": "error",
|
|
18
|
+
"prefer-const": "error",
|
|
19
|
+
"no-var": "error",
|
|
20
|
+
"@typescript-eslint/no-explicit-any": "warn"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"ignorePatterns": [
|
|
24
|
+
"**/node_modules/**",
|
|
25
|
+
"**/dist/**",
|
|
26
|
+
"**/build/**",
|
|
27
|
+
"**/coverage/**",
|
|
28
|
+
"**/*.d.ts"
|
|
29
|
+
],
|
|
30
|
+
"languages": ["typescript", "javascript"],
|
|
31
|
+
"include": ["src/**", "tests/**"],
|
|
32
|
+
"exclude": ["**/node_modules/**"],
|
|
33
|
+
"output": {
|
|
34
|
+
"format": "eslint",
|
|
35
|
+
"console": true,
|
|
36
|
+
"summary": true
|
|
37
|
+
},
|
|
38
|
+
"ai": {
|
|
39
|
+
"enabled": false,
|
|
40
|
+
"fallbackToPattern": true
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# SunLint Configuration Examples
|
|
2
|
+
|
|
3
|
+
This directory contains example configurations and workflows for SunLint integration.
|
|
4
|
+
|
|
5
|
+
## Configuration Examples
|
|
6
|
+
|
|
7
|
+
### 📋 **Project Configuration**
|
|
8
|
+
- **`.sunlint.json`** - Complete project configuration example
|
|
9
|
+
- **`package.json`** - NPM scripts and dependencies setup
|
|
10
|
+
|
|
11
|
+
### 🚀 **CI/CD Examples**
|
|
12
|
+
- **`.github/workflows/code-quality.yml`** - GitHub Actions workflow
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
### Copy Configuration
|
|
17
|
+
```bash
|
|
18
|
+
# Copy base configuration to your project
|
|
19
|
+
cp examples/.sunlint.json your-project/.sunlint.json
|
|
20
|
+
|
|
21
|
+
# Copy GitHub Actions workflow
|
|
22
|
+
cp examples/.github/workflows/code-quality.yml .github/workflows/
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Customize Rules
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"extends": ["@sun/sunlint/recommended"],
|
|
29
|
+
"rules": {
|
|
30
|
+
"C006": "error", // Function naming
|
|
31
|
+
"C019": "error", // Log level usage
|
|
32
|
+
"S005": "warn", // Security rules
|
|
33
|
+
"S012": "error" // No hardcoded secrets
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Integration Patterns
|
|
39
|
+
|
|
40
|
+
1. **TypeScript Projects**: Use with ESLint integration
|
|
41
|
+
2. **CI/CD**: GitHub Actions with changed files only
|
|
42
|
+
3. **Pre-commit**: Staged files validation
|
|
43
|
+
4. **Full scan**: Baseline creation for large projects
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
**For test projects, see `test/` directory**
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "example-project",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"lint": "npm run lint:eslint && npm run lint:sunlint",
|
|
6
|
+
"lint:eslint": "eslint src/",
|
|
7
|
+
"lint:sunlint": "sunlint --all --input=src",
|
|
8
|
+
"lint:changed": "npm run lint:eslint:changed && npm run lint:sunlint:changed",
|
|
9
|
+
"lint:eslint:changed": "eslint $(git diff --name-only --diff-filter=AM origin/main | grep -E '\\.(js|ts|tsx)$' | tr '\\n' ' ')",
|
|
10
|
+
"lint:sunlint:changed": "sunlint --all --changed-files --diff-base=origin/main",
|
|
11
|
+
"lint:fix": "npm run lint:eslint:fix && npm run lint:sunlint:fix",
|
|
12
|
+
"lint:eslint:fix": "eslint src/ --fix",
|
|
13
|
+
"lint:sunlint:fix": "sunlint --all --input=src --fix"
|
|
14
|
+
},
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"@sun/sunlint": "^1.0.5",
|
|
17
|
+
"eslint": "^8.57.0"
|
|
18
|
+
},
|
|
19
|
+
"sunlint": {
|
|
20
|
+
"extends": ["@sun/sunlint/recommended"],
|
|
21
|
+
"rules": {
|
|
22
|
+
"S003": "warn",
|
|
23
|
+
"C006": "error",
|
|
24
|
+
"S047": "warn"
|
|
25
|
+
},
|
|
26
|
+
"ignorePatterns": ["**/test/**", "**/cypress/**"],
|
|
27
|
+
"eslint": {
|
|
28
|
+
"enabled": true,
|
|
29
|
+
"runBefore": true,
|
|
30
|
+
"configPath": ".eslintrc.js"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sun-asterisk/sunlint",
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "☀️ Sun Lint - Universal Coding Standards | Multi-rule Quality & Security Analysis with ESLint Integration",
|
|
5
|
+
"main": "cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sunlint": "./cli.js"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/sun-asterisk/engineer-excellence.git",
|
|
12
|
+
"directory": "coding-quality/extensions/sunlint"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"registry": "https://registry.npmjs.org",
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "npm run test:unit && npm run test:integration",
|
|
20
|
+
"test:unit": "node test/unit/test-runner.js",
|
|
21
|
+
"test:integration": "node test/integration/test-runner.js",
|
|
22
|
+
"test:c019": "node cli.js --rule=C019 --input=test/fixtures --format=eslint",
|
|
23
|
+
"test:c006": "node cli.js --rule=C006 --input=test/fixtures --format=eslint",
|
|
24
|
+
"test:c029": "node cli.js --rule=C029 --input=test/fixtures/c029 --format=eslint",
|
|
25
|
+
"test:multi": "node cli.js --rules=C019,C006,C029 --input=test/fixtures --format=eslint",
|
|
26
|
+
"test:all": "node cli.js --all --input=test/fixtures --format=eslint",
|
|
27
|
+
"test:quality": "node cli.js --category=quality --input=test/fixtures --format=eslint",
|
|
28
|
+
"test:security": "node cli.js --category=security --input=test/fixtures --format=eslint",
|
|
29
|
+
"demo": "./demo.sh",
|
|
30
|
+
"demo:single": "node cli.js --rule=C019 --input=test/fixtures/typescript --format=eslint",
|
|
31
|
+
"demo:multi": "node cli.js --rules=C019,C006 --input=test/fixtures --format=summary",
|
|
32
|
+
"demo:quality": "node cli.js --quality --input=test/fixtures --format=summary",
|
|
33
|
+
"demo:security": "node cli.js --security --input=test/fixtures --format=summary",
|
|
34
|
+
"demo:all": "node cli.js --all --input=test/fixtures --format=summary",
|
|
35
|
+
"demo:config": "node cli.js --config=.sunlint.json --input=test/fixtures",
|
|
36
|
+
"demo:eslint-integration": "./demo-eslint-integration.sh",
|
|
37
|
+
"lint": "node cli.js --config=.sunlint.json --input=.",
|
|
38
|
+
"lint:eslint-integration": "node cli.js --all --eslint-integration --input=.",
|
|
39
|
+
"build": "echo 'No build step required for Node.js CLI'",
|
|
40
|
+
"clean": "rm -rf coverage/ *.log reports/ *.tgz",
|
|
41
|
+
"start": "node cli.js --help",
|
|
42
|
+
"version": "node cli.js --version",
|
|
43
|
+
"pack": "npm pack",
|
|
44
|
+
"publish:github": "npm publish --registry=https://npm.pkg.github.com",
|
|
45
|
+
"publish:npmjs": "npm publish --registry=https://registry.npmjs.org",
|
|
46
|
+
"publish:test": "npm publish --dry-run --registry=https://registry.npmjs.org",
|
|
47
|
+
"prepublishOnly": "npm run clean"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"linting",
|
|
51
|
+
"code-quality",
|
|
52
|
+
"security",
|
|
53
|
+
"typescript",
|
|
54
|
+
"eslint",
|
|
55
|
+
"eslint-integration",
|
|
56
|
+
"coding-standards",
|
|
57
|
+
"static-analysis",
|
|
58
|
+
"git-integration",
|
|
59
|
+
"ci-cd",
|
|
60
|
+
"sun-asterisk"
|
|
61
|
+
],
|
|
62
|
+
"author": "Sun* Engineering Team",
|
|
63
|
+
"license": "MIT",
|
|
64
|
+
"files": [
|
|
65
|
+
"cli.js",
|
|
66
|
+
"cli-legacy.js",
|
|
67
|
+
"core/",
|
|
68
|
+
"rules/",
|
|
69
|
+
"config/",
|
|
70
|
+
"eslint-integration/",
|
|
71
|
+
"docs/",
|
|
72
|
+
"examples/",
|
|
73
|
+
"README.md",
|
|
74
|
+
"LICENSE",
|
|
75
|
+
"CHANGELOG.md"
|
|
76
|
+
],
|
|
77
|
+
"engines": {
|
|
78
|
+
"node": ">=18.18.0"
|
|
79
|
+
},
|
|
80
|
+
"dependencies": {
|
|
81
|
+
"@typescript-eslint/eslint-plugin": "^8.36.0",
|
|
82
|
+
"@typescript-eslint/parser": "^8.36.0",
|
|
83
|
+
"chalk": "^4.1.2",
|
|
84
|
+
"commander": "^12.1.0",
|
|
85
|
+
"eslint": "^9.30.0",
|
|
86
|
+
"glob": "^11.0.0",
|
|
87
|
+
"minimatch": "^10.0.3",
|
|
88
|
+
"node-fetch": "^3.3.2",
|
|
89
|
+
"table": "^6.8.2",
|
|
90
|
+
"typescript": "^5.7.2"
|
|
91
|
+
},
|
|
92
|
+
"devDependencies": {
|
|
93
|
+
"@types/node": "^22.10.1",
|
|
94
|
+
"jest": "^29.7.0"
|
|
95
|
+
},
|
|
96
|
+
"bugs": {
|
|
97
|
+
"url": "https://github.com/sun-asterisk/engineer-excellence/issues"
|
|
98
|
+
},
|
|
99
|
+
"homepage": "https://github.com/sun-asterisk/engineer-excellence/tree/main/coding-quality/extensions/sunlint#readme"
|
|
100
|
+
}
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const ts = require('typescript');
|
|
4
|
+
|
|
5
|
+
class C006Analyzer {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.ruleId = 'C006';
|
|
8
|
+
this.ruleName = 'Function Naming Convention';
|
|
9
|
+
this.description = 'Tên hàm phải là động từ/verb-noun pattern';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async analyze(files, language, config) {
|
|
13
|
+
const violations = [];
|
|
14
|
+
|
|
15
|
+
for (const filePath of files) {
|
|
16
|
+
try {
|
|
17
|
+
const fileContent = fs.readFileSync(filePath, 'utf8');
|
|
18
|
+
const fileViolations = await this.analyzeFile(filePath, fileContent, language, config);
|
|
19
|
+
violations.push(...fileViolations);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error(`Error analyzing file ${filePath}:`, error.message);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return violations;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async analyzeFile(filePath, content, language, config) {
|
|
29
|
+
switch (language) {
|
|
30
|
+
case 'typescript':
|
|
31
|
+
case 'javascript':
|
|
32
|
+
return this.analyzeTypeScript(filePath, content, config);
|
|
33
|
+
case 'dart':
|
|
34
|
+
return this.analyzeDart(filePath, content, config);
|
|
35
|
+
case 'kotlin':
|
|
36
|
+
return this.analyzeKotlin(filePath, content, config);
|
|
37
|
+
default:
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async analyzeTypeScript(filePath, content, config) {
|
|
43
|
+
const violations = [];
|
|
44
|
+
const lines = content.split('\n');
|
|
45
|
+
|
|
46
|
+
// Parse TypeScript/JavaScript code
|
|
47
|
+
const sourceFile = ts.createSourceFile(
|
|
48
|
+
filePath,
|
|
49
|
+
content,
|
|
50
|
+
ts.ScriptTarget.Latest,
|
|
51
|
+
true
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const visit = (node) => {
|
|
55
|
+
// Check function declarations (skip declare statements without body)
|
|
56
|
+
if (ts.isFunctionDeclaration(node) && node.name && node.body) {
|
|
57
|
+
const functionName = node.name.text;
|
|
58
|
+
const analysis = this.analyzeFunctionName(functionName);
|
|
59
|
+
|
|
60
|
+
if (analysis.isViolation) {
|
|
61
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
62
|
+
const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
|
|
63
|
+
const lineText = lines[line - 1]?.trim() || '';
|
|
64
|
+
|
|
65
|
+
violations.push({
|
|
66
|
+
ruleId: this.ruleId,
|
|
67
|
+
file: filePath,
|
|
68
|
+
line,
|
|
69
|
+
column,
|
|
70
|
+
message: analysis.reason,
|
|
71
|
+
severity: analysis.severity || 'warning',
|
|
72
|
+
code: lineText,
|
|
73
|
+
type: analysis.type,
|
|
74
|
+
confidence: analysis.confidence || 0.8,
|
|
75
|
+
suggestion: analysis.suggestion
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Check method declarations
|
|
81
|
+
if (ts.isMethodDeclaration(node) && node.name) {
|
|
82
|
+
const methodName = node.name.text;
|
|
83
|
+
const analysis = this.analyzeFunctionName(methodName);
|
|
84
|
+
|
|
85
|
+
if (analysis.isViolation) {
|
|
86
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
87
|
+
const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
|
|
88
|
+
const lineText = lines[line - 1]?.trim() || '';
|
|
89
|
+
|
|
90
|
+
violations.push({
|
|
91
|
+
ruleId: this.ruleId,
|
|
92
|
+
file: filePath,
|
|
93
|
+
line,
|
|
94
|
+
column,
|
|
95
|
+
message: analysis.reason,
|
|
96
|
+
severity: analysis.severity || 'warning',
|
|
97
|
+
code: lineText,
|
|
98
|
+
type: analysis.type,
|
|
99
|
+
confidence: analysis.confidence || 0.8,
|
|
100
|
+
suggestion: analysis.suggestion
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check arrow functions assigned to variables
|
|
106
|
+
if (ts.isVariableDeclaration(node) && node.name && ts.isIdentifier(node.name) &&
|
|
107
|
+
node.initializer && ts.isArrowFunction(node.initializer)) {
|
|
108
|
+
const functionName = node.name.text;
|
|
109
|
+
const analysis = this.analyzeFunctionName(functionName);
|
|
110
|
+
|
|
111
|
+
if (analysis.isViolation) {
|
|
112
|
+
const line = sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1;
|
|
113
|
+
const column = sourceFile.getLineAndCharacterOfPosition(node.getStart()).character + 1;
|
|
114
|
+
const lineText = lines[line - 1]?.trim() || '';
|
|
115
|
+
|
|
116
|
+
violations.push({
|
|
117
|
+
ruleId: this.ruleId,
|
|
118
|
+
file: filePath,
|
|
119
|
+
line,
|
|
120
|
+
column,
|
|
121
|
+
message: analysis.reason,
|
|
122
|
+
severity: analysis.severity || 'warning',
|
|
123
|
+
code: lineText,
|
|
124
|
+
type: analysis.type,
|
|
125
|
+
confidence: analysis.confidence || 0.8,
|
|
126
|
+
suggestion: analysis.suggestion
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
ts.forEachChild(node, visit);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
visit(sourceFile);
|
|
135
|
+
return violations;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async analyzeDart(filePath, content, config) {
|
|
139
|
+
const violations = [];
|
|
140
|
+
const lines = content.split('\n');
|
|
141
|
+
|
|
142
|
+
// Pattern-based analysis for Dart function declarations
|
|
143
|
+
const functionPattern = /^\s*(\w+\s+)?(\w+)\s*\([^)]*\)\s*\{?/;
|
|
144
|
+
|
|
145
|
+
lines.forEach((line, index) => {
|
|
146
|
+
const lineNumber = index + 1;
|
|
147
|
+
const match = line.match(functionPattern);
|
|
148
|
+
|
|
149
|
+
if (match) {
|
|
150
|
+
const functionName = match[2];
|
|
151
|
+
|
|
152
|
+
// Skip constructors, getters, setters, and built-in methods
|
|
153
|
+
if (this.isSpecialFunction(functionName)) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const analysis = this.analyzeFunctionName(functionName);
|
|
158
|
+
|
|
159
|
+
if (analysis.isViolation) {
|
|
160
|
+
violations.push({
|
|
161
|
+
ruleId: this.ruleId,
|
|
162
|
+
file: filePath,
|
|
163
|
+
line: lineNumber,
|
|
164
|
+
column: line.indexOf(functionName) + 1,
|
|
165
|
+
message: analysis.reason,
|
|
166
|
+
severity: analysis.severity || 'warning',
|
|
167
|
+
code: line.trim(),
|
|
168
|
+
type: analysis.type,
|
|
169
|
+
confidence: analysis.confidence || 0.7,
|
|
170
|
+
suggestion: analysis.suggestion
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return violations;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async analyzeKotlin(filePath, content, config) {
|
|
180
|
+
const violations = [];
|
|
181
|
+
const lines = content.split('\n');
|
|
182
|
+
|
|
183
|
+
// Pattern-based analysis for Kotlin function declarations
|
|
184
|
+
const functionPattern = /^\s*(?:fun|private\s+fun|public\s+fun|internal\s+fun|override\s+fun)\s+(\w+)\s*\(/;
|
|
185
|
+
|
|
186
|
+
lines.forEach((line, index) => {
|
|
187
|
+
const lineNumber = index + 1;
|
|
188
|
+
const match = line.match(functionPattern);
|
|
189
|
+
|
|
190
|
+
if (match) {
|
|
191
|
+
const functionName = match[1];
|
|
192
|
+
|
|
193
|
+
// Skip constructors and built-in methods
|
|
194
|
+
if (this.isSpecialFunction(functionName)) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const analysis = this.analyzeFunctionName(functionName);
|
|
199
|
+
|
|
200
|
+
if (analysis.isViolation) {
|
|
201
|
+
violations.push({
|
|
202
|
+
ruleId: this.ruleId,
|
|
203
|
+
file: filePath,
|
|
204
|
+
line: lineNumber,
|
|
205
|
+
column: line.indexOf(functionName) + 1,
|
|
206
|
+
message: analysis.reason,
|
|
207
|
+
severity: analysis.severity || 'warning',
|
|
208
|
+
code: line.trim(),
|
|
209
|
+
type: analysis.type,
|
|
210
|
+
confidence: analysis.confidence || 0.7,
|
|
211
|
+
suggestion: analysis.suggestion
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
return violations;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
analyzeFunctionName(functionName) {
|
|
221
|
+
// Skip special functions
|
|
222
|
+
if (this.isSpecialFunction(functionName)) {
|
|
223
|
+
return { isViolation: false };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Common verbs for function names
|
|
227
|
+
const commonVerbs = [
|
|
228
|
+
'get', 'set', 'is', 'has', 'can', 'should', 'will',
|
|
229
|
+
'create', 'build', 'make', 'generate', 'construct',
|
|
230
|
+
'update', 'modify', 'change', 'edit', 'alter',
|
|
231
|
+
'delete', 'remove', 'destroy', 'clean', 'clear',
|
|
232
|
+
'load', 'save', 'fetch', 'retrieve', 'find', 'search',
|
|
233
|
+
'validate', 'verify', 'check', 'confirm', 'ensure',
|
|
234
|
+
'calculate', 'compute', 'process', 'handle', 'manage',
|
|
235
|
+
'send', 'receive', 'transmit', 'broadcast', 'emit',
|
|
236
|
+
'parse', 'format', 'transform', 'convert', 'map',
|
|
237
|
+
'filter', 'sort', 'group', 'merge', 'split',
|
|
238
|
+
'connect', 'disconnect', 'open', 'close', 'start', 'stop',
|
|
239
|
+
'show', 'hide', 'display', 'render', 'draw', 'paint',
|
|
240
|
+
'add', 'append', 'insert', 'push', 'pop', 'shift',
|
|
241
|
+
'test', 'debug', 'log', 'trace', 'monitor', 'watch',
|
|
242
|
+
'count', 'execute', 'perform', 'run', 'invoke', 'call',
|
|
243
|
+
'reset', 'initialize', 'setup', 'configure', 'prepare',
|
|
244
|
+
'refresh', 'restore', 'reload', 'retry', 'resume', 'redirect',
|
|
245
|
+
'select', 'toggle', 'switch', 'enable', 'disable', 'activate',
|
|
246
|
+
'expand', 'collapse', 'scroll', 'navigate', 'submit', 'cancel'
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
// Check if function name starts with a verb
|
|
250
|
+
const startsWithVerb = commonVerbs.some(verb => {
|
|
251
|
+
const verbPattern = new RegExp(`^${verb}[A-Z]?`, 'i');
|
|
252
|
+
return verbPattern.test(functionName);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
if (startsWithVerb) {
|
|
256
|
+
return { isViolation: false };
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Check for camelCase pattern that might be verb-noun
|
|
260
|
+
const camelCasePattern = /^[a-z][a-zA-Z0-9]*$/;
|
|
261
|
+
const pascalCasePattern = /^[A-Z][a-zA-Z0-9]*$/;
|
|
262
|
+
|
|
263
|
+
// PascalCase functions (likely React components) - different rules
|
|
264
|
+
if (pascalCasePattern.test(functionName)) {
|
|
265
|
+
// For React components, we can be more lenient but still suggest verb patterns where appropriate
|
|
266
|
+
return {
|
|
267
|
+
isViolation: true,
|
|
268
|
+
reason: 'PascalCase function should be a React component or use verb-noun pattern',
|
|
269
|
+
severity: 'info',
|
|
270
|
+
type: 'react_component_naming',
|
|
271
|
+
confidence: 0.5,
|
|
272
|
+
suggestion: `If this is a React component, consider using descriptive names. If it's a function, use camelCase with verb-noun pattern.`
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!camelCasePattern.test(functionName)) {
|
|
277
|
+
return {
|
|
278
|
+
isViolation: true,
|
|
279
|
+
reason: 'Function name should follow camelCase and verb-noun pattern',
|
|
280
|
+
severity: 'warning',
|
|
281
|
+
type: 'naming_convention',
|
|
282
|
+
confidence: 0.9,
|
|
283
|
+
suggestion: 'Use camelCase with verb-noun pattern (e.g., getUserData, validateInput)'
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Check for noun-only patterns (likely violations)
|
|
288
|
+
const nounOnlyPatterns = [
|
|
289
|
+
/^[a-z]+$/, // simple lowercase (e.g., 'user', 'data')
|
|
290
|
+
/^[a-z]+[A-Z][a-z]+$/, // simple camelCase noun (e.g., 'userData', 'userInfo')
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
const isNounOnly = nounOnlyPatterns.some(pattern => pattern.test(functionName));
|
|
294
|
+
|
|
295
|
+
if (isNounOnly) {
|
|
296
|
+
return {
|
|
297
|
+
isViolation: true,
|
|
298
|
+
reason: 'Function name appears to be a noun - should start with a verb',
|
|
299
|
+
severity: 'warning',
|
|
300
|
+
type: 'missing_verb',
|
|
301
|
+
confidence: 0.8,
|
|
302
|
+
suggestion: `Consider renaming to use verb-noun pattern (e.g., get${functionName.charAt(0).toUpperCase() + functionName.slice(1)})`
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// If it's a complex name without obvious verb, flag as potential violation
|
|
307
|
+
if (functionName.length > 3 && !functionName.match(/^[a-z]+[A-Z]/)) {
|
|
308
|
+
return {
|
|
309
|
+
isViolation: true,
|
|
310
|
+
reason: 'Function name should follow verb-noun pattern for clarity',
|
|
311
|
+
severity: 'info',
|
|
312
|
+
type: 'unclear_naming',
|
|
313
|
+
confidence: 0.6,
|
|
314
|
+
suggestion: 'Consider using verb-noun pattern for better readability'
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return { isViolation: false };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
isSpecialFunction(name) {
|
|
322
|
+
const specialFunctions = [
|
|
323
|
+
'constructor', 'toString', 'valueOf', 'toJSON',
|
|
324
|
+
'main', 'init', 'setup', 'teardown', 'build',
|
|
325
|
+
'onCreate', 'onDestroy', 'onStart', 'onStop',
|
|
326
|
+
'onPause', 'onResume', 'onSaveInstanceState',
|
|
327
|
+
'equals', 'hashCode', 'compareTo', 'clone',
|
|
328
|
+
'finalize', 'notify', 'notifyAll', 'wait'
|
|
329
|
+
];
|
|
330
|
+
|
|
331
|
+
return specialFunctions.includes(name) ||
|
|
332
|
+
name.startsWith('_') ||
|
|
333
|
+
name.startsWith('$');
|
|
334
|
+
// Removed: || name.match(/^[A-Z]/); // Allow checking React components
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
module.exports = new C006Analyzer();
|