@sun-asterisk/sunlint 1.1.3 → 1.1.4
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/.sunlint.json +1 -1
- package/README.md +77 -10
- package/config/eslint-rule-mapping.json +1 -1
- package/config/rules/rules-registry.json +8 -7
- package/core/ast-modules/parsers/eslint-js-parser.js +27 -21
- package/core/ast-modules/parsers/eslint-ts-parser.js +28 -36
- package/core/dependency-checker.js +125 -0
- package/core/smart-installer.js +164 -0
- package/docs/DEPENDENCIES.md +90 -0
- package/docs/FUTURE_PACKAGES.md +83 -0
- package/docs/PRODUCTION_DEPLOYMENT_ANALYSIS.md +112 -0
- package/docs/PRODUCTION_SIZE_IMPACT.md +183 -0
- package/engines/eslint-engine.js +9 -0
- package/engines/heuristic-engine.js +4 -0
- package/package.json +34 -7
- package/integrations/eslint/test-c041-rule.js +0 -87
package/.sunlint.json
CHANGED
package/README.md
CHANGED
|
@@ -8,29 +8,30 @@ Sun Lint is a universal coding standards checker providing comprehensive code qu
|
|
|
8
8
|
|
|
9
9
|
### **✨ Key Features**
|
|
10
10
|
- ✅ **97+ Coding Rules**: Quality (30), Security (47), TypeScript-specific
|
|
11
|
-
- ✅ **
|
|
11
|
+
- ✅ **Built-in AST Analysis**: JavaScript/TypeScript parsing out of the box
|
|
12
12
|
- ✅ **Multi-Engine Architecture**: Heuristic + ESLint + OpenAI integration
|
|
13
13
|
- ✅ **Git Integration**: `--changed-files`, `--staged-files`, `--pr-mode`
|
|
14
14
|
- ✅ **TypeScript Support**: Native TypeScript 5.8+ analysis
|
|
15
|
+
- ✅ **Zero Config**: Works immediately after `npm install`
|
|
15
16
|
- ✅ **CI/CD Ready**: Baseline comparison, fail-on-new-violations
|
|
16
17
|
- ✅ **Advanced File Targeting**: Include/exclude patterns, language filtering
|
|
17
18
|
|
|
18
19
|
### **🚀 Quick Start**
|
|
19
20
|
```bash
|
|
20
|
-
# Install
|
|
21
|
+
# Install
|
|
21
22
|
npm install -g @sun-asterisk/sunlint
|
|
22
23
|
|
|
23
|
-
# Basic usage
|
|
24
|
+
# Basic usage - works immediately!
|
|
24
25
|
sunlint --all
|
|
25
26
|
sunlint --rules=C019,C006
|
|
26
27
|
|
|
27
|
-
#
|
|
28
|
+
# With input specification
|
|
28
29
|
sunlint --all --input=src
|
|
29
30
|
sunlint --rules=C019,C006 --input=src
|
|
30
31
|
sunlint --quality --input=src
|
|
31
32
|
sunlint --security --input=src
|
|
32
33
|
|
|
33
|
-
# ESLint integration (
|
|
34
|
+
# ESLint integration (requires eslint dependency)
|
|
34
35
|
sunlint --rules=C010,C006 --eslint-integration --input=src
|
|
35
36
|
|
|
36
37
|
# Git integration
|
|
@@ -48,12 +49,66 @@ sunlint --version
|
|
|
48
49
|
### **Project Installation**
|
|
49
50
|
```bash
|
|
50
51
|
npm install --save-dev @sun-asterisk/sunlint
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**✅ Works immediately** with JavaScript analysis using built-in AST parsers (`@babel/parser`, `espree`)
|
|
55
|
+
|
|
56
|
+
### **Enhanced TypeScript Support**
|
|
57
|
+
For advanced TypeScript analysis with ESLint integration:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npm install --save-dev @sun-asterisk/sunlint eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### **What's Included by Default**
|
|
64
|
+
- ✅ **JavaScript Analysis**: High-accuracy AST analysis out of the box
|
|
65
|
+
- ✅ **Basic TypeScript**: Works with built-in Babel parser
|
|
66
|
+
- ✅ **97+ Rules**: All quality and security rules available
|
|
67
|
+
- ✅ **Heuristic Engine**: Pattern-based analysis for all languages
|
|
68
|
+
|
|
69
|
+
### **Optional Dependencies (Install as needed)**
|
|
70
|
+
```bash
|
|
71
|
+
# For ESLint engine integration
|
|
72
|
+
npm install eslint --save-dev
|
|
73
|
+
|
|
74
|
+
# For enhanced TypeScript analysis
|
|
75
|
+
npm install @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
|
|
76
|
+
|
|
77
|
+
# For TypeScript compiler integration
|
|
78
|
+
npm install typescript --save-dev
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Quick setup for TypeScript projects:**
|
|
82
|
+
```bash
|
|
83
|
+
npm install --save-dev @sun-asterisk/sunlint eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
> 💡 **Note**: SunLint gracefully handles missing dependencies. Install only what your project needs. See [docs/DEPENDENCIES.md](docs/DEPENDENCIES.md) for detailed guidance.
|
|
51
87
|
|
|
52
88
|
# Package.json scripts
|
|
89
|
+
```json
|
|
53
90
|
{
|
|
54
91
|
"scripts": {
|
|
55
92
|
"lint": "sunlint --all --input=src",
|
|
56
|
-
"lint:changed": "sunlint --all --changed-files"
|
|
93
|
+
"lint:changed": "sunlint --all --changed-files",
|
|
94
|
+
"lint:typescript": "sunlint --all --input=src",
|
|
95
|
+
"lint:eslint-integration": "sunlint --all --eslint-integration --input=src"
|
|
96
|
+
},
|
|
97
|
+
"devDependencies": {
|
|
98
|
+
"@sun-asterisk/sunlint": "^1.2.0"
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
**For TypeScript projects, add:**
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"devDependencies": {
|
|
107
|
+
"@sun-asterisk/sunlint": "^1.2.0",
|
|
108
|
+
"eslint": "^8.50.0",
|
|
109
|
+
"@typescript-eslint/parser": "^7.2.0",
|
|
110
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
111
|
+
"typescript": "^5.0.0"
|
|
57
112
|
}
|
|
58
113
|
}
|
|
59
114
|
```
|
|
@@ -209,16 +264,28 @@ Complete reference with all available options:
|
|
|
209
264
|
|
|
210
265
|
### **Development**
|
|
211
266
|
```bash
|
|
212
|
-
#
|
|
213
|
-
|
|
267
|
+
# Quick start - works immediately
|
|
268
|
+
npm install --save-dev @sun-asterisk/sunlint
|
|
269
|
+
npx sunlint --all --input=src
|
|
214
270
|
|
|
215
271
|
# Check specific rules
|
|
216
272
|
sunlint --rules=C019,S005 --input=src
|
|
217
273
|
|
|
218
|
-
# ESLint
|
|
274
|
+
# ESLint integration (requires eslint dependency)
|
|
275
|
+
npm install --save-dev eslint
|
|
219
276
|
sunlint --all --eslint-integration --changed-files
|
|
220
277
|
```
|
|
221
278
|
|
|
279
|
+
### **TypeScript Projects**
|
|
280
|
+
```bash
|
|
281
|
+
# Enhanced TypeScript setup
|
|
282
|
+
npm install --save-dev @sun-asterisk/sunlint eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript
|
|
283
|
+
|
|
284
|
+
# Full TypeScript analysis
|
|
285
|
+
sunlint --all --input=src
|
|
286
|
+
sunlint --all --eslint-integration --input=src
|
|
287
|
+
```
|
|
288
|
+
|
|
222
289
|
### **CI/CD**
|
|
223
290
|
```bash
|
|
224
291
|
# Full project scan
|
|
@@ -227,7 +294,7 @@ sunlint --all --input=. --format=json --output=report.json
|
|
|
227
294
|
# PR validation
|
|
228
295
|
sunlint --all --changed-files --fail-on-new-violations
|
|
229
296
|
|
|
230
|
-
# Pre-commit hook
|
|
297
|
+
# Pre-commit hook
|
|
231
298
|
sunlint --all --staged-files --format=summary
|
|
232
299
|
```
|
|
233
300
|
|
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
"R001": ["react/no-this-in-sfc", "no-param-reassign", "react/function-component-definition", "react/forbid-component-props"],
|
|
105
105
|
"R002": ["react-hooks/rules-of-hooks", "react-hooks/exhaustive-deps", "react/no-did-mount-set-state", "react/no-did-update-set-state"],
|
|
106
106
|
"R003": ["react/no-direct-mutation-state", "react/jsx-no-constructed-context-values", "react/forbid-dom-props"],
|
|
107
|
-
"R004": ["
|
|
107
|
+
"R004": ["no-param-reassign", "react/forbid-foreign-prop-types"],
|
|
108
108
|
"R005": ["react/jsx-no-bind"],
|
|
109
109
|
"R006": ["react/jsx-pascal-case", "react/jsx-uses-react", "react/jsx-uses-vars"],
|
|
110
110
|
"R007": ["react-hooks/rules-of-hooks"],
|
|
@@ -651,13 +651,13 @@
|
|
|
651
651
|
"quality": {
|
|
652
652
|
"name": "Code Quality",
|
|
653
653
|
"description": "Rules for code quality improvement",
|
|
654
|
-
"rules": ["C002", "C003", "C006", "C010", "C013", "C014", "C017", "C018", "
|
|
654
|
+
"rules": ["C002", "C003", "C006", "C010", "C013", "C014", "C017", "C018", "C023", "C029", "C030", "C035", "C041", "C042", "C043", "C047", "C072", "C075", "C076", "T002", "T003", "T004", "T007", "T010", "T019", "T020", "T021", "R001", "R002", "R003", "R004", "R005", "R006"],
|
|
655
655
|
"severity": "warning"
|
|
656
656
|
},
|
|
657
657
|
"security": {
|
|
658
658
|
"name": "Security",
|
|
659
659
|
"description": "Rules for security best practices",
|
|
660
|
-
"rules": ["S003", "S005", "S006", "S008", "S009", "S010", "S011", "S012", "S014", "S015", "S016", "S017", "S018", "S019", "S020", "S022", "S023", "S025", "S026", "S027", "S029", "S030", "S033", "S034", "S035", "S036", "S037", "S038", "S039", "S041", "S042", "S043", "S044", "S045", "S046", "S047", "S048", "S050", "S052", "S054", "S055", "S057", "S058"],
|
|
660
|
+
"rules": ["S001", "S002", "S003", "S005", "S006", "S007", "S008", "S009", "S010", "S011", "S012", "S013", "S014", "S015", "S016", "S017", "S018", "S019", "S020", "S022", "S023", "S025", "S026", "S027", "S029", "S030", "S033", "S034", "S035", "S036", "S037", "S038", "S039", "S041", "S042", "S043", "S044", "S045", "S046", "S047", "S048", "S050", "S052", "S054", "S055", "S057", "S058"],
|
|
661
661
|
"severity": "error"
|
|
662
662
|
},
|
|
663
663
|
"logging": {
|
|
@@ -745,11 +745,11 @@
|
|
|
745
745
|
}
|
|
746
746
|
},
|
|
747
747
|
"metadata": {
|
|
748
|
-
"version": "1.
|
|
749
|
-
"lastUpdated": "2025-07-
|
|
748
|
+
"version": "1.1.4",
|
|
749
|
+
"lastUpdated": "2025-07-24",
|
|
750
750
|
"totalRules": 44,
|
|
751
|
-
"qualityRules":
|
|
752
|
-
"securityRules":
|
|
751
|
+
"qualityRules": 33,
|
|
752
|
+
"securityRules": 47,
|
|
753
753
|
"stableRules": 43,
|
|
754
754
|
"experimentalRules": 1,
|
|
755
755
|
"supportedLanguages": 4,
|
|
@@ -757,7 +757,8 @@
|
|
|
757
757
|
"Security rules integration",
|
|
758
758
|
"Category-based rule filtering",
|
|
759
759
|
"Dynamic rule configuration",
|
|
760
|
-
"ESLint integration enhancement"
|
|
760
|
+
"ESLint integration enhancement",
|
|
761
|
+
"React rules integration"
|
|
761
762
|
]
|
|
762
763
|
}
|
|
763
764
|
}
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ESLint-based JavaScript Parser
|
|
3
3
|
* Uses the same AST infrastructure as ESLint for reliable parsing
|
|
4
|
+
* Gracefully handles missing peer dependencies
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
class ESLintJavaScriptParser {
|
|
7
8
|
constructor() {
|
|
8
9
|
this.parser = null;
|
|
10
|
+
this.parserType = null;
|
|
9
11
|
this.initParser();
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
initParser() {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// Try to dynamically load available parsers
|
|
16
|
+
const parsers = [
|
|
17
|
+
{ name: '@babel/parser', type: 'babel' },
|
|
18
|
+
{ name: 'espree', type: 'espree' }
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
for (const { name, type } of parsers) {
|
|
17
22
|
try {
|
|
18
|
-
|
|
19
|
-
this.
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
this.parser = require(name);
|
|
24
|
+
this.parserType = type;
|
|
25
|
+
break;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
// Continue to next parser
|
|
28
|
+
continue;
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
31
|
}
|
|
@@ -28,11 +34,9 @@ class ESLintJavaScriptParser {
|
|
|
28
34
|
if (!this.parser) return null;
|
|
29
35
|
|
|
30
36
|
try {
|
|
31
|
-
if (this.
|
|
32
|
-
// @babel/parser or espree
|
|
37
|
+
if (this.parserType === 'babel') {
|
|
33
38
|
return this.parser.parse(code, {
|
|
34
|
-
|
|
35
|
-
sourceType: 'module',
|
|
39
|
+
sourceType: 'unambiguous',
|
|
36
40
|
allowImportExportEverywhere: true,
|
|
37
41
|
allowReturnOutsideFunction: true,
|
|
38
42
|
plugins: [
|
|
@@ -43,14 +47,16 @@ class ESLintJavaScriptParser {
|
|
|
43
47
|
'objectRestSpread',
|
|
44
48
|
'optionalChaining',
|
|
45
49
|
'nullishCoalescingOperator'
|
|
46
|
-
]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
} else if (this.parserType === 'espree') {
|
|
53
|
+
return this.parser.parse(code, {
|
|
54
|
+
ecmaVersion: 'latest',
|
|
55
|
+
sourceType: 'module',
|
|
56
|
+
ecmaFeatures: {
|
|
57
|
+
jsx: true,
|
|
58
|
+
globalReturn: true
|
|
59
|
+
}
|
|
54
60
|
});
|
|
55
61
|
}
|
|
56
62
|
} catch (error) {
|
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ESLint-based TypeScript Parser
|
|
3
3
|
* Uses @typescript-eslint/parser for TypeScript AST analysis
|
|
4
|
+
* Gracefully handles missing peer dependencies
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
7
|
class ESLintTypeScriptParser {
|
|
7
8
|
constructor() {
|
|
8
9
|
this.parser = null;
|
|
10
|
+
this.parserType = null;
|
|
9
11
|
this.initParser();
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
initParser() {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
// Try to dynamically load available TypeScript parsers
|
|
16
|
+
const parsers = [
|
|
17
|
+
{ name: '@typescript-eslint/parser', type: 'typescript-eslint' },
|
|
18
|
+
{ name: '@babel/parser', type: 'babel' }
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
for (const { name, type } of parsers) {
|
|
17
22
|
try {
|
|
18
|
-
|
|
19
|
-
this.
|
|
20
|
-
|
|
21
|
-
} catch (
|
|
22
|
-
//
|
|
23
|
-
|
|
23
|
+
this.parser = require(name);
|
|
24
|
+
this.parserType = type;
|
|
25
|
+
break;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
// Continue to next parser
|
|
28
|
+
continue;
|
|
24
29
|
}
|
|
25
30
|
}
|
|
26
31
|
}
|
|
@@ -29,45 +34,32 @@ class ESLintTypeScriptParser {
|
|
|
29
34
|
if (!this.parser) return null;
|
|
30
35
|
|
|
31
36
|
try {
|
|
32
|
-
// Try Babel parser first for better TypeScript 5.x support
|
|
33
37
|
if (this.parserType === 'babel') {
|
|
34
38
|
return this.parser.parse(code, {
|
|
35
|
-
sourceType: '
|
|
39
|
+
sourceType: 'unambiguous',
|
|
36
40
|
allowImportExportEverywhere: true,
|
|
37
41
|
plugins: ['typescript', 'jsx', 'decorators-legacy']
|
|
38
42
|
});
|
|
39
|
-
} else {
|
|
40
|
-
//
|
|
43
|
+
} else if (this.parserType === 'typescript-eslint') {
|
|
44
|
+
// First try @babel/parser for better compatibility
|
|
41
45
|
try {
|
|
42
46
|
const babel = require('@babel/parser');
|
|
43
47
|
return babel.parse(code, {
|
|
44
|
-
sourceType: '
|
|
48
|
+
sourceType: 'unambiguous',
|
|
45
49
|
allowImportExportEverywhere: true,
|
|
46
50
|
plugins: ['typescript', 'jsx', 'decorators-legacy', 'classProperties']
|
|
47
51
|
});
|
|
48
52
|
} catch (babelError) {
|
|
49
|
-
// Fallback to @typescript-eslint/parser
|
|
53
|
+
// Fallback to @typescript-eslint/parser if available
|
|
50
54
|
if (this.parser.parseForESLint) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
sourceType: 'module',
|
|
60
|
-
ecmaFeatures: {
|
|
61
|
-
jsx: true
|
|
62
|
-
},
|
|
63
|
-
errorOnUnknownASTType: false // Don't error on unknown AST types
|
|
64
|
-
});
|
|
65
|
-
return result.ast;
|
|
66
|
-
} finally {
|
|
67
|
-
// Always restore console methods
|
|
68
|
-
console.warn = originalWarn;
|
|
69
|
-
console.error = originalError;
|
|
70
|
-
}
|
|
55
|
+
const result = this.parser.parseForESLint(code, {
|
|
56
|
+
ecmaVersion: 'latest',
|
|
57
|
+
sourceType: 'module',
|
|
58
|
+
ecmaFeatures: {
|
|
59
|
+
jsx: true
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return result.ast;
|
|
71
63
|
}
|
|
72
64
|
}
|
|
73
65
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dependency Checker
|
|
3
|
+
* Checks for optional peer dependencies and provides helpful messages
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
class DependencyChecker {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.checkedDependencies = new Set();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Check if a dependency is available
|
|
13
|
+
*/
|
|
14
|
+
isDependencyAvailable(packageName) {
|
|
15
|
+
try {
|
|
16
|
+
require.resolve(packageName);
|
|
17
|
+
return true;
|
|
18
|
+
} catch (error) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Check for ESLint engine dependencies
|
|
25
|
+
*/
|
|
26
|
+
checkESLintDependencies() {
|
|
27
|
+
const dependencies = {
|
|
28
|
+
'eslint': { required: false, description: 'ESLint engine support' },
|
|
29
|
+
'@typescript-eslint/eslint-plugin': { required: false, description: 'TypeScript ESLint rules' },
|
|
30
|
+
'@typescript-eslint/parser': { required: false, description: 'TypeScript ESLint parsing' },
|
|
31
|
+
'typescript': { required: false, description: 'TypeScript compiler' }
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const missing = [];
|
|
35
|
+
const available = [];
|
|
36
|
+
|
|
37
|
+
for (const [pkg, info] of Object.entries(dependencies)) {
|
|
38
|
+
if (this.isDependencyAvailable(pkg)) {
|
|
39
|
+
available.push(pkg);
|
|
40
|
+
} else {
|
|
41
|
+
missing.push({ pkg, ...info });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return { available, missing };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check for AST parsing dependencies
|
|
50
|
+
*/
|
|
51
|
+
checkASTDependencies() {
|
|
52
|
+
const dependencies = {
|
|
53
|
+
'@babel/parser': { required: false, description: 'JavaScript AST parsing' },
|
|
54
|
+
'espree': { required: false, description: 'ECMAScript AST parsing' }
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const missing = [];
|
|
58
|
+
const available = [];
|
|
59
|
+
|
|
60
|
+
for (const [pkg, info] of Object.entries(dependencies)) {
|
|
61
|
+
if (this.isDependencyAvailable(pkg)) {
|
|
62
|
+
available.push(pkg);
|
|
63
|
+
} else {
|
|
64
|
+
missing.push({ pkg, ...info });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return { available, missing };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Show installation instructions for missing dependencies
|
|
73
|
+
*/
|
|
74
|
+
showInstallationInstructions(missing, context = 'general') {
|
|
75
|
+
if (missing.length === 0) return;
|
|
76
|
+
|
|
77
|
+
console.log('\n📦 Optional dependencies not found:');
|
|
78
|
+
|
|
79
|
+
for (const { pkg, description } of missing) {
|
|
80
|
+
console.log(` • ${pkg} - ${description}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const packages = missing.map(m => m.pkg).join(' ');
|
|
84
|
+
|
|
85
|
+
if (context === 'eslint') {
|
|
86
|
+
console.log('\n💡 To enable ESLint engine features, install:');
|
|
87
|
+
console.log(` npm install ${packages}`);
|
|
88
|
+
console.log('\n Or add to your project dependencies if you already have them.');
|
|
89
|
+
} else if (context === 'ast') {
|
|
90
|
+
console.log('\n💡 To enable AST analysis features, install:');
|
|
91
|
+
console.log(` npm install ${packages}`);
|
|
92
|
+
} else {
|
|
93
|
+
console.log('\n💡 To enable full functionality, install:');
|
|
94
|
+
console.log(` npm install ${packages}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log('\n SunLint will continue using heuristic analysis.\n');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Check and notify about missing dependencies (only once per session)
|
|
102
|
+
*/
|
|
103
|
+
checkAndNotify(type = 'all') {
|
|
104
|
+
const key = `checked_${type}`;
|
|
105
|
+
if (this.checkedDependencies.has(key)) return;
|
|
106
|
+
|
|
107
|
+
this.checkedDependencies.add(key);
|
|
108
|
+
|
|
109
|
+
if (type === 'eslint' || type === 'all') {
|
|
110
|
+
const { missing } = this.checkESLintDependencies();
|
|
111
|
+
if (missing.length > 0) {
|
|
112
|
+
this.showInstallationInstructions(missing, 'eslint');
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (type === 'ast' || type === 'all') {
|
|
117
|
+
const { missing } = this.checkASTDependencies();
|
|
118
|
+
if (missing.length > 0) {
|
|
119
|
+
this.showInstallationInstructions(missing, 'ast');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
module.exports = new DependencyChecker();
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Smart Dependency Auto-Installer
|
|
3
|
+
* Automatically installs missing peer dependencies when SunLint runs
|
|
4
|
+
* Future: Will support package flavor recommendations
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
|
|
11
|
+
class SmartInstaller {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.installedInSession = new Set();
|
|
14
|
+
this.packageFlavors = {
|
|
15
|
+
'typescript': '@sun-asterisk/sunlint-typescript',
|
|
16
|
+
'dart': '@sun-asterisk/sunlint-dart',
|
|
17
|
+
'python': '@sun-asterisk/sunlint-python',
|
|
18
|
+
'go': '@sun-asterisk/sunlint-go',
|
|
19
|
+
'full': '@sun-asterisk/sunlint-full'
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Detect project type and recommend appropriate packages
|
|
25
|
+
*/
|
|
26
|
+
detectProjectType(projectRoot) {
|
|
27
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
28
|
+
if (!fs.existsSync(packageJsonPath)) return ['basic'];
|
|
29
|
+
|
|
30
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
31
|
+
const types = [];
|
|
32
|
+
|
|
33
|
+
// Check for TypeScript
|
|
34
|
+
if (packageJson.devDependencies?.typescript ||
|
|
35
|
+
packageJson.dependencies?.typescript ||
|
|
36
|
+
fs.existsSync(path.join(projectRoot, 'tsconfig.json'))) {
|
|
37
|
+
types.push('typescript');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Check for other languages (future)
|
|
41
|
+
if (fs.existsSync(path.join(projectRoot, 'pubspec.yaml'))) {
|
|
42
|
+
types.push('dart');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (fs.existsSync(path.join(projectRoot, 'requirements.txt')) ||
|
|
46
|
+
fs.existsSync(path.join(projectRoot, 'pyproject.toml'))) {
|
|
47
|
+
types.push('python');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (fs.existsSync(path.join(projectRoot, 'go.mod'))) {
|
|
51
|
+
types.push('go');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return types.length > 0 ? types : ['basic'];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Recommend optimal package flavors instead of individual dependencies
|
|
59
|
+
*/
|
|
60
|
+
recommendPackageFlavors(missingDeps, projectTypes) {
|
|
61
|
+
const recommendations = [];
|
|
62
|
+
|
|
63
|
+
// If missing TypeScript deps and it's a TS project
|
|
64
|
+
if (missingDeps.some(d => d.pkg.includes('@typescript-eslint')) &&
|
|
65
|
+
projectTypes.includes('typescript')) {
|
|
66
|
+
recommendations.push({
|
|
67
|
+
package: '@sun-asterisk/sunlint-typescript',
|
|
68
|
+
reason: 'Complete TypeScript analysis support',
|
|
69
|
+
replaces: missingDeps.filter(d => d.pkg.includes('typescript')).map(d => d.pkg)
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// If missing many ESLint deps, suggest full package
|
|
74
|
+
if (missingDeps.length >= 3 && missingDeps.some(d => d.pkg === 'eslint')) {
|
|
75
|
+
recommendations.push({
|
|
76
|
+
package: '@sun-asterisk/sunlint-full',
|
|
77
|
+
reason: 'Complete ESLint integration with all features',
|
|
78
|
+
replaces: missingDeps.map(d => d.pkg)
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return recommendations;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Check if we're in a project that can install dependencies
|
|
87
|
+
*/
|
|
88
|
+
canAutoInstall() {
|
|
89
|
+
// Check if package.json exists in current or parent directories
|
|
90
|
+
let currentDir = process.cwd();
|
|
91
|
+
const root = path.parse(currentDir).root;
|
|
92
|
+
|
|
93
|
+
while (currentDir !== root) {
|
|
94
|
+
if (fs.existsSync(path.join(currentDir, 'package.json'))) {
|
|
95
|
+
return currentDir;
|
|
96
|
+
}
|
|
97
|
+
currentDir = path.dirname(currentDir);
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Auto-install missing dependencies with user confirmation
|
|
104
|
+
*/
|
|
105
|
+
async autoInstallMissing(missingDeps, context = 'analysis') {
|
|
106
|
+
const projectRoot = this.canAutoInstall();
|
|
107
|
+
if (!projectRoot) {
|
|
108
|
+
this.showManualInstallInstructions(missingDeps);
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
console.log(`\n🔍 SunLint needs these dependencies for enhanced ${context}:`);
|
|
113
|
+
missingDeps.forEach(dep => console.log(` • ${dep.pkg} - ${dep.description}`));
|
|
114
|
+
|
|
115
|
+
const packages = missingDeps.map(d => d.pkg).join(' ');
|
|
116
|
+
|
|
117
|
+
console.log(`\n💡 Install command:`);
|
|
118
|
+
console.log(` npm install ${packages} --save-dev`);
|
|
119
|
+
|
|
120
|
+
// In CI environments, don't auto-install but show clear message
|
|
121
|
+
if (process.env.CI || process.env.NODE_ENV === 'test') {
|
|
122
|
+
console.log('\n⚠️ CI Environment: Add dependencies to package.json for consistent builds');
|
|
123
|
+
console.log(' SunLint will continue with available features\n');
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Interactive prompt for auto-install
|
|
128
|
+
const shouldAutoInstall = process.env.SUNLINT_AUTO_INSTALL === 'true';
|
|
129
|
+
|
|
130
|
+
if (shouldAutoInstall) {
|
|
131
|
+
try {
|
|
132
|
+
console.log('\n📦 Auto-installing dependencies...');
|
|
133
|
+
execSync(`npm install ${packages} --save-dev`, {
|
|
134
|
+
cwd: projectRoot,
|
|
135
|
+
stdio: 'pipe' // Less noisy
|
|
136
|
+
});
|
|
137
|
+
console.log('✅ Dependencies installed successfully!');
|
|
138
|
+
console.log('🔄 Re-running analysis with full features...\n');
|
|
139
|
+
return true;
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.log('❌ Auto-install failed, continuing with available features');
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
console.log('\n🔄 Continuing with available features...');
|
|
146
|
+
console.log('💡 Set SUNLINT_AUTO_INSTALL=true to enable automatic installation\n');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Show manual installation instructions
|
|
154
|
+
*/
|
|
155
|
+
showManualInstallInstructions(missingDeps) {
|
|
156
|
+
console.log('\n📦 To enable full functionality, install:');
|
|
157
|
+
const packages = missingDeps.map(d => d.pkg).join(' ');
|
|
158
|
+
console.log(` npm install ${packages} --save-dev`);
|
|
159
|
+
console.log('\n💡 Or set SUNLINT_AUTO_INSTALL=true for automatic installation');
|
|
160
|
+
console.log(' SunLint will continue with heuristic analysis.\n');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
module.exports = new SmartInstaller();
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Installing SunLint Dependencies
|
|
2
|
+
|
|
3
|
+
SunLint is designed to work immediately after installation with built-in JavaScript/TypeScript analysis capabilities. Additional dependencies are only needed for enhanced features.
|
|
4
|
+
|
|
5
|
+
## Core Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @sun-asterisk/sunlint --save-dev
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**✅ What you get immediately:**
|
|
12
|
+
- High-accuracy JavaScript analysis (AST-based)
|
|
13
|
+
- Basic TypeScript analysis (Babel parser)
|
|
14
|
+
- All 97+ quality and security rules
|
|
15
|
+
- Heuristic analysis for all supported languages
|
|
16
|
+
|
|
17
|
+
## Enhanced Features
|
|
18
|
+
|
|
19
|
+
### For ESLint Integration
|
|
20
|
+
|
|
21
|
+
If you want to combine SunLint with ESLint rules and ecosystem:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install eslint --save-dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### For Advanced TypeScript Analysis
|
|
28
|
+
|
|
29
|
+
For enhanced TypeScript parsing and ESLint TypeScript rules:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @typescript-eslint/eslint-plugin @typescript-eslint/parser --save-dev
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### For TypeScript Projects (Complete Setup)
|
|
36
|
+
|
|
37
|
+
For full TypeScript development support:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install eslint typescript @typescript-eslint/eslint-plugin @typescript-eslint/parser --save-dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Installation Examples
|
|
44
|
+
|
|
45
|
+
### Minimal Setup (JavaScript projects)
|
|
46
|
+
```bash
|
|
47
|
+
npm install @sun-asterisk/sunlint --save-dev
|
|
48
|
+
npx sunlint --all --input=src # ✅ Works immediately
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### TypeScript Projects
|
|
52
|
+
```bash
|
|
53
|
+
npm install @sun-asterisk/sunlint eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin typescript --save-dev
|
|
54
|
+
npx sunlint --all --input=src # ✅ Full TypeScript support
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### ESLint Integration
|
|
58
|
+
```bash
|
|
59
|
+
npm install @sun-asterisk/sunlint eslint --save-dev
|
|
60
|
+
npx sunlint --all --eslint-integration --input=src # ✅ Combined analysis
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## What happens without optional dependencies?
|
|
64
|
+
|
|
65
|
+
- **Without ESLint**: SunLint uses heuristic engine only (still very capable)
|
|
66
|
+
- **Without TypeScript parsers**: Falls back to Babel parser (good coverage)
|
|
67
|
+
- **Without TypeScript compiler**: Basic type checking only
|
|
68
|
+
|
|
69
|
+
**SunLint always provides analysis results** - dependencies only enhance capabilities.
|
|
70
|
+
|
|
71
|
+
## Built-in vs Optional Parsers
|
|
72
|
+
|
|
73
|
+
### ✅ Built-in (Always Available)
|
|
74
|
+
- **@babel/parser**: JavaScript + basic TypeScript support
|
|
75
|
+
- **espree**: ECMAScript parsing for compatibility
|
|
76
|
+
|
|
77
|
+
### 🔄 Optional (Install as needed)
|
|
78
|
+
- **@typescript-eslint/parser**: Advanced TypeScript features
|
|
79
|
+
- **@typescript-eslint/eslint-plugin**: TypeScript-specific rules
|
|
80
|
+
- **eslint**: ESLint engine integration
|
|
81
|
+
- **typescript**: TypeScript compiler integration
|
|
82
|
+
|
|
83
|
+
## Dependency Check
|
|
84
|
+
|
|
85
|
+
Run any SunLint command to see recommendations for your project:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx sunlint --help
|
|
89
|
+
# Automatically detects project type and suggests relevant dependencies
|
|
90
|
+
```
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Future SunLint Package Strategy
|
|
2
|
+
|
|
3
|
+
## Core Packages
|
|
4
|
+
|
|
5
|
+
### @sun-asterisk/sunlint (Core)
|
|
6
|
+
- Size: ~235KB
|
|
7
|
+
- Features: Basic JS/TS heuristic analysis
|
|
8
|
+
- Dependencies: @babel/parser, espree
|
|
9
|
+
- Target: Minimal setup, basic quality checks
|
|
10
|
+
|
|
11
|
+
### @sun-asterisk/sunlint-typescript
|
|
12
|
+
- Size: ~2MB
|
|
13
|
+
- Features: Full TypeScript analysis with AST
|
|
14
|
+
- Dependencies: Core + @typescript-eslint/parser, typescript
|
|
15
|
+
- Target: TypeScript projects
|
|
16
|
+
|
|
17
|
+
### @sun-asterisk/sunlint-full
|
|
18
|
+
- Size: ~10MB
|
|
19
|
+
- Features: Complete ESLint integration, all parsers
|
|
20
|
+
- Dependencies: Core + all ESLint ecosystem
|
|
21
|
+
- Target: Full-featured analysis
|
|
22
|
+
|
|
23
|
+
## Language Extensions
|
|
24
|
+
|
|
25
|
+
### @sun-asterisk/sunlint-dart
|
|
26
|
+
- Dart-specific rules and analysis
|
|
27
|
+
- Dependencies: dart_analyzer, dart tools
|
|
28
|
+
|
|
29
|
+
### @sun-asterisk/sunlint-python
|
|
30
|
+
- Python-specific rules
|
|
31
|
+
- Dependencies: pylint, autopep8, mypy
|
|
32
|
+
|
|
33
|
+
### @sun-asterisk/sunlint-go
|
|
34
|
+
- Go-specific analysis
|
|
35
|
+
- Dependencies: staticcheck, golint
|
|
36
|
+
|
|
37
|
+
## Feature Extensions
|
|
38
|
+
|
|
39
|
+
### @sun-asterisk/sunlint-ai
|
|
40
|
+
- AI-powered code analysis
|
|
41
|
+
- Dependencies: OpenAI API, Claude API
|
|
42
|
+
|
|
43
|
+
### @sun-asterisk/sunlint-security
|
|
44
|
+
- Advanced security scanning
|
|
45
|
+
- Dependencies: semgrep, snyk, security plugins
|
|
46
|
+
|
|
47
|
+
## Installation Examples
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Basic usage
|
|
51
|
+
npm install @sun-asterisk/sunlint --save-dev
|
|
52
|
+
|
|
53
|
+
# TypeScript project
|
|
54
|
+
npm install @sun-asterisk/sunlint-typescript --save-dev
|
|
55
|
+
|
|
56
|
+
# Multi-language project
|
|
57
|
+
npm install @sun-asterisk/sunlint @sun-asterisk/sunlint-dart @sun-asterisk/sunlint-python --save-dev
|
|
58
|
+
|
|
59
|
+
# Enterprise setup with all features
|
|
60
|
+
npm install @sun-asterisk/sunlint-full @sun-asterisk/sunlint-ai @sun-asterisk/sunlint-security --save-dev
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Configuration
|
|
64
|
+
|
|
65
|
+
### Package-based auto-detection
|
|
66
|
+
```json
|
|
67
|
+
// .sunlint.json
|
|
68
|
+
{
|
|
69
|
+
"extends": ["auto-detect-packages"], // Automatically use installed packages
|
|
70
|
+
"rules": {
|
|
71
|
+
"C010": "error"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Smart recommendations
|
|
77
|
+
```bash
|
|
78
|
+
# When SunLint detects TypeScript files but no TS package
|
|
79
|
+
npx sunlint --all
|
|
80
|
+
# Output:
|
|
81
|
+
# 💡 TypeScript files detected. For better analysis:
|
|
82
|
+
# npm install @sun-asterisk/sunlint-typescript --save-dev
|
|
83
|
+
```
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Production Deployment Analysis - SunLint Size Impact
|
|
2
|
+
|
|
3
|
+
## 🔍 **Câu hỏi từ Developer:**
|
|
4
|
+
*"Công việc trên có phải là phổ biến không, tức là khi deploy thông thường có làm các công việc đó không?"*
|
|
5
|
+
|
|
6
|
+
## 📊 **Thực tế Deployment Phổ Biến**
|
|
7
|
+
|
|
8
|
+
### ✅ **THỰC TẾ 1: DevDependencies tự động bị loại trừ**
|
|
9
|
+
|
|
10
|
+
**Hầu hết deployment platforms tự động loại trừ devDependencies:**
|
|
11
|
+
|
|
12
|
+
| Platform | Auto-exclude devDependencies | Command |
|
|
13
|
+
|----------|------------------------------|---------|
|
|
14
|
+
| **Heroku** | ✅ | `npm install --production` |
|
|
15
|
+
| **Vercel** | ✅ | `npm ci --production` |
|
|
16
|
+
| **Netlify** | ✅ | `npm install --production` |
|
|
17
|
+
| **Docker** | ✅ | `RUN npm ci --only=production` |
|
|
18
|
+
| **AWS Lambda** | ✅ | `npm install --production` |
|
|
19
|
+
| **Google Cloud** | ✅ | `npm ci --production` |
|
|
20
|
+
|
|
21
|
+
### ✅ **THỰC TẾ 2: Build process tự nhiên**
|
|
22
|
+
|
|
23
|
+
**Typical CI/CD pipeline:**
|
|
24
|
+
```bash
|
|
25
|
+
# Development
|
|
26
|
+
npm install # Cài tất cả (bao gồm SunLint)
|
|
27
|
+
npm run lint # SunLint chạy để check code
|
|
28
|
+
npm run test # Tests chạy
|
|
29
|
+
npm run build # Build production bundle
|
|
30
|
+
|
|
31
|
+
# Production deployment
|
|
32
|
+
npm ci --production # CHỈ cài runtime dependencies
|
|
33
|
+
# SunLint tự động bị loại trừ!
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### ✅ **THỰC TẾ 3: Docker multistage builds**
|
|
37
|
+
|
|
38
|
+
**Phổ biến nhất - Docker multistage:**
|
|
39
|
+
```dockerfile
|
|
40
|
+
# Build stage
|
|
41
|
+
FROM node:18 AS builder
|
|
42
|
+
COPY package*.json ./
|
|
43
|
+
RUN npm install # Cài tất cả, kể cả SunLint
|
|
44
|
+
COPY . .
|
|
45
|
+
RUN npm run build # SunLint chạy trong quá trình build
|
|
46
|
+
|
|
47
|
+
# Production stage
|
|
48
|
+
FROM node:18-alpine AS production
|
|
49
|
+
COPY package*.json ./
|
|
50
|
+
RUN npm ci --production --silent # CHỈ runtime deps
|
|
51
|
+
COPY --from=builder /app/dist ./dist
|
|
52
|
+
# SunLint không có trong production image!
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 🎯 **Kết luận về SunLint**
|
|
56
|
+
|
|
57
|
+
### **TẠI SAO SUNLINT AN TOÀN 100%:**
|
|
58
|
+
|
|
59
|
+
1. **DevDependency by design** - SunLint được thiết kế như linting tool
|
|
60
|
+
2. **Industry standard** - Tất cả linting tools đều hoạt động như vậy
|
|
61
|
+
3. **Automatic exclusion** - Deployment platforms tự động loại trừ
|
|
62
|
+
4. **Zero production footprint** - Không code SunLint nào trong runtime
|
|
63
|
+
|
|
64
|
+
### **SO SÁNH VỚI CÔNG CỤ KHÁC:**
|
|
65
|
+
|
|
66
|
+
| Tool | Size | Production Impact | Usage Pattern |
|
|
67
|
+
|------|------|-------------------|---------------|
|
|
68
|
+
| **ESLint** | ~500KB | ❌ (nếu sai cách) | DevDependency ✅ |
|
|
69
|
+
| **SunLint** | **241KB** | ✅ **KHÔNG** | DevDependency ✅ |
|
|
70
|
+
| **Prettier** | ~300KB | ❌ (nếu sai cách) | DevDependency ✅ |
|
|
71
|
+
| **TypeScript** | ~60MB | ❌ (nếu sai cách) | DevDependency ✅ |
|
|
72
|
+
|
|
73
|
+
## 📈 **Best Practices Verification**
|
|
74
|
+
|
|
75
|
+
### **✅ ĐÚNG CÁCH (99% cases):**
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"devDependencies": {
|
|
79
|
+
"@sun-asterisk/sunlint": "^1.1.4",
|
|
80
|
+
"eslint": "^8.57.1",
|
|
81
|
+
"prettier": "^3.0.0"
|
|
82
|
+
},
|
|
83
|
+
"dependencies": {
|
|
84
|
+
"express": "^4.18.0" // Chỉ runtime deps
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### **❌ SAI CÁCH (hiếm, chỉ newbie):**
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"dependencies": {
|
|
93
|
+
"@sun-asterisk/sunlint": "^1.1.4", // ❌ Sai!
|
|
94
|
+
"express": "^4.18.0"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 🚀 **Tóm tắt**
|
|
100
|
+
|
|
101
|
+
**CÂU TRẢ LỜI:** Các công việc này **HOÀN TOÀN PHỔ BIẾN** và **TỰ ĐỘNG** trong production deployment:
|
|
102
|
+
|
|
103
|
+
1. ✅ **DevDependencies tự động bị loại trừ** - Industry standard
|
|
104
|
+
2. ✅ **Build tools chỉ chạy development time** - Normal practice
|
|
105
|
+
3. ✅ **Production chỉ có runtime code** - Security best practice
|
|
106
|
+
4. ✅ **SunLint = 0KB trong production** - Guaranteed
|
|
107
|
+
|
|
108
|
+
**Developer không cần lo lắng về size impact của SunLint!**
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
*Generated on: 2025-07-24*
|
|
112
|
+
*SunLint Version: 1.1.4*
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# 📦 SunLint Production Size Impact Analysis
|
|
2
|
+
|
|
3
|
+
## Tóm tắt cho Leadership
|
|
4
|
+
|
|
5
|
+
**KẾT LUẬN: SunLint KHÔNG làm tăng size production khi sử dụng đúng cách.**
|
|
6
|
+
|
|
7
|
+
## Chi tiết phân tích
|
|
8
|
+
|
|
9
|
+
### 1. Package Size của SunLint
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
SunLint package size: 241.6 kB
|
|
13
|
+
SunLint unpacked size: 1.1 MB
|
|
14
|
+
Total files: 214
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
### 2. Test Production Impact
|
|
18
|
+
|
|
19
|
+
Chúng tôi đã tạo một project test để kiểm tra impact thực tế:
|
|
20
|
+
|
|
21
|
+
| Giai đoạn | Size | Ghi chú |
|
|
22
|
+
|-----------|------|---------|
|
|
23
|
+
| Project ban đầu | 8.0K | Chỉ có package.json và .gitignore |
|
|
24
|
+
| Sau khi cài SunLint (devDependency) | 88M | Bao gồm tất cả devDependencies |
|
|
25
|
+
| Production bundle (dist/) | 4.0K | Code production thực tế |
|
|
26
|
+
| Sau npm prune --production | 156K | Đã xóa tất cả devDependencies |
|
|
27
|
+
|
|
28
|
+
### 3. Khuyến nghị sử dụng trong Production
|
|
29
|
+
|
|
30
|
+
#### ✅ ĐÚNG CÁCH (Production-friendly):
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@sun-asterisk/sunlint": "^1.1.4"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Lợi ích:**
|
|
41
|
+
- ✅ Không ảnh hưởng size production bundle
|
|
42
|
+
- ✅ Chỉ cài khi development (`npm install`)
|
|
43
|
+
- ✅ Tự động loại trừ khỏi production (`npm prune --production`)
|
|
44
|
+
- ✅ CI/CD có thể dùng để check code quality
|
|
45
|
+
|
|
46
|
+
#### ❌ SAI CÁCH (Không khuyến nghị):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@sun-asterisk/sunlint": "^1.1.4"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Vấn đề:**
|
|
57
|
+
- ❌ Tăng 1.1MB cho production bundle
|
|
58
|
+
- ❌ Không cần thiết cho runtime
|
|
59
|
+
- ❌ Làm chậm deployment
|
|
60
|
+
|
|
61
|
+
### 4. Deployment Strategies
|
|
62
|
+
|
|
63
|
+
#### Option 1: Development Only (Khuyến nghị)
|
|
64
|
+
```bash
|
|
65
|
+
# Development
|
|
66
|
+
npm install
|
|
67
|
+
|
|
68
|
+
# Production build
|
|
69
|
+
npm run build
|
|
70
|
+
npm prune --production
|
|
71
|
+
# → SunLint sẽ bị xóa hoàn toàn
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Option 2: CI/CD Pipeline
|
|
75
|
+
```yaml
|
|
76
|
+
# .github/workflows/ci.yml
|
|
77
|
+
- name: Install deps
|
|
78
|
+
run: npm ci
|
|
79
|
+
- name: Run SunLint
|
|
80
|
+
run: npx sunlint --quality --input=src
|
|
81
|
+
- name: Build production
|
|
82
|
+
run: npm run build
|
|
83
|
+
- name: Remove dev deps
|
|
84
|
+
run: npm prune --production
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Option 3: Docker Multi-stage
|
|
88
|
+
```dockerfile
|
|
89
|
+
# Development stage với SunLint
|
|
90
|
+
FROM node:18 as dev
|
|
91
|
+
COPY package*.json ./
|
|
92
|
+
RUN npm ci
|
|
93
|
+
COPY . .
|
|
94
|
+
RUN npx sunlint --all --input=src
|
|
95
|
+
|
|
96
|
+
# Production stage KHÔNG có SunLint
|
|
97
|
+
FROM node:18-alpine as prod
|
|
98
|
+
COPY package*.json ./
|
|
99
|
+
RUN npm ci --only=production
|
|
100
|
+
COPY dist/ ./dist/
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 5. So sánh với các tools khác
|
|
104
|
+
|
|
105
|
+
| Tool | Package Size | Production Impact | Use Case |
|
|
106
|
+
|------|-------------|-------------------|-----------|
|
|
107
|
+
| ESLint | ~500KB | ❌ Nếu để dependencies | Development only |
|
|
108
|
+
| Prettier | ~200KB | ❌ Nếu để dependencies | Development only |
|
|
109
|
+
| **SunLint** | **241KB** | **✅ KHÔNG (devDep)** | **Development + CI/CD** |
|
|
110
|
+
| TypeScript | ~60MB | ❌ Nếu để dependencies | Development only |
|
|
111
|
+
|
|
112
|
+
### 6. Best Practices cho Teams
|
|
113
|
+
|
|
114
|
+
#### Developers:
|
|
115
|
+
```bash
|
|
116
|
+
# Local development
|
|
117
|
+
npm install # Cài tất cả deps (bao gồm SunLint)
|
|
118
|
+
npx sunlint --quality --input=src
|
|
119
|
+
|
|
120
|
+
# Pre-commit hook
|
|
121
|
+
npx sunlint --changed-files
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### CI/CD:
|
|
125
|
+
```bash
|
|
126
|
+
# Build pipeline
|
|
127
|
+
npm ci # Cài tất cả deps
|
|
128
|
+
npx sunlint --all --input=src # Quality check
|
|
129
|
+
npm run build # Build production
|
|
130
|
+
npm prune --production # Xóa devDeps
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
#### Production:
|
|
134
|
+
```bash
|
|
135
|
+
# Server deployment
|
|
136
|
+
npm ci --only=production # Chỉ cài production deps
|
|
137
|
+
# → SunLint sẽ KHÔNG được cài
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### 7. Monitoring & Verification
|
|
141
|
+
|
|
142
|
+
#### Verify production size:
|
|
143
|
+
```bash
|
|
144
|
+
# Trước deploy
|
|
145
|
+
du -sh node_modules
|
|
146
|
+
du -sh dist/
|
|
147
|
+
|
|
148
|
+
# Kiểm tra không có SunLint
|
|
149
|
+
ls node_modules | grep sunlint # Không có kết quả = OK
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
#### Bundle size monitoring:
|
|
153
|
+
```bash
|
|
154
|
+
# Add to package.json
|
|
155
|
+
{
|
|
156
|
+
"scripts": {
|
|
157
|
+
"analyze-bundle": "du -sh dist/ && echo 'Production bundle size'",
|
|
158
|
+
"verify-prod": "npm ls --production --depth=0"
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Kết luận
|
|
164
|
+
|
|
165
|
+
### ✅ AN TOÀN cho Production:
|
|
166
|
+
- SunLint được thiết kế như devDependency
|
|
167
|
+
- Không ảnh hưởng size production bundle khi sử dụng đúng
|
|
168
|
+
- Có thể tích hợp vào CI/CD mà không ảnh hưởng deployment
|
|
169
|
+
|
|
170
|
+
### 📊 Số liệu cụ thể:
|
|
171
|
+
- **Development**: +88MB (chỉ khi dev)
|
|
172
|
+
- **Production**: +0KB (khi dùng đúng cách)
|
|
173
|
+
- **CI/CD**: Impact chỉ ở build time, không ở runtime
|
|
174
|
+
|
|
175
|
+
### 🚀 Khuyến nghị:
|
|
176
|
+
1. **Luôn cài như devDependency**
|
|
177
|
+
2. **Sử dụng trong CI/CD pipeline**
|
|
178
|
+
3. **npm prune --production trước deploy**
|
|
179
|
+
4. **Monitor bundle size định kỳ**
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
*Tài liệu này được cập nhật cho SunLint v1.1.4 - July 2025*
|
package/engines/eslint-engine.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
const AnalysisEngineInterface = require('../core/interfaces/analysis-engine.interface');
|
|
9
|
+
const dependencyChecker = require('../core/dependency-checker');
|
|
9
10
|
const fs = require('fs');
|
|
10
11
|
const path = require('path');
|
|
11
12
|
|
|
@@ -62,6 +63,9 @@ class ESLintEngine extends AnalysisEngineInterface {
|
|
|
62
63
|
// Store verbosity setting for use in other methods
|
|
63
64
|
this.verbose = config?.verbose || false;
|
|
64
65
|
|
|
66
|
+
// Check for ESLint dependencies first
|
|
67
|
+
dependencyChecker.checkAndNotify('eslint');
|
|
68
|
+
|
|
65
69
|
// Dynamically import ESLint
|
|
66
70
|
const { ESLint } = await this.loadESLint();
|
|
67
71
|
|
|
@@ -94,6 +98,11 @@ class ESLintEngine extends AnalysisEngineInterface {
|
|
|
94
98
|
* @returns {Promise<Object>} ESLint module
|
|
95
99
|
*/
|
|
96
100
|
async loadESLint() {
|
|
101
|
+
// Check if ESLint is available first
|
|
102
|
+
if (!dependencyChecker.isDependencyAvailable('eslint')) {
|
|
103
|
+
throw new Error('ESLint not available. Install with: npm install eslint');
|
|
104
|
+
}
|
|
105
|
+
|
|
97
106
|
try {
|
|
98
107
|
// Try to load ESLint from node_modules
|
|
99
108
|
return await import('eslint');
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
const AnalysisEngineInterface = require('../core/interfaces/analysis-engine.interface');
|
|
9
9
|
const ASTModuleRegistry = require('../core/ast-modules/index');
|
|
10
|
+
const dependencyChecker = require('../core/dependency-checker');
|
|
10
11
|
const fs = require('fs');
|
|
11
12
|
const path = require('path');
|
|
12
13
|
|
|
@@ -30,6 +31,9 @@ class HeuristicEngine extends AnalysisEngineInterface {
|
|
|
30
31
|
// Store verbosity setting
|
|
31
32
|
this.verbose = config?.verbose || false;
|
|
32
33
|
|
|
34
|
+
// Check for optional AST dependencies
|
|
35
|
+
dependencyChecker.checkAndNotify('ast');
|
|
36
|
+
|
|
33
37
|
// Load rules registry
|
|
34
38
|
await this.loadRulesRegistry(config);
|
|
35
39
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sunlint",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "☀️
|
|
3
|
+
"version": "1.1.4",
|
|
4
|
+
"description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
|
|
5
5
|
"main": "cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sunlint": "./cli.js"
|
|
@@ -87,22 +87,49 @@
|
|
|
87
87
|
},
|
|
88
88
|
"dependencies": {
|
|
89
89
|
"@babel/parser": "^7.23.0",
|
|
90
|
-
"@typescript-eslint/
|
|
90
|
+
"@typescript-eslint/eslint-plugin": "^7.18.0",
|
|
91
|
+
"@typescript-eslint/parser": "^7.18.0",
|
|
91
92
|
"chalk": "^4.1.2",
|
|
92
93
|
"commander": "^12.1.0",
|
|
94
|
+
"eslint": "^8.57.1",
|
|
95
|
+
"eslint-plugin-react": "^7.37.5",
|
|
96
|
+
"eslint-plugin-react-hooks": "^5.2.0",
|
|
93
97
|
"espree": "^9.6.0",
|
|
94
|
-
"glob": "^11.0.0",
|
|
95
98
|
"minimatch": "^10.0.3",
|
|
96
99
|
"node-fetch": "^3.3.2",
|
|
97
|
-
"table": "^6.8.2"
|
|
98
|
-
|
|
100
|
+
"table": "^6.8.2"
|
|
101
|
+
},
|
|
102
|
+
"peerDependencies": {
|
|
103
|
+
"typescript": ">=4.0.0"
|
|
104
|
+
},
|
|
105
|
+
"peerDependenciesMeta": {
|
|
106
|
+
"eslint": {
|
|
107
|
+
"optional": true
|
|
108
|
+
},
|
|
109
|
+
"@typescript-eslint/eslint-plugin": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
112
|
+
"@typescript-eslint/parser": {
|
|
113
|
+
"optional": true
|
|
114
|
+
},
|
|
115
|
+
"eslint-plugin-react": {
|
|
116
|
+
"optional": true
|
|
117
|
+
},
|
|
118
|
+
"eslint-plugin-react-hooks": {
|
|
119
|
+
"optional": true
|
|
120
|
+
},
|
|
121
|
+
"typescript": {
|
|
122
|
+
"optional": true
|
|
123
|
+
}
|
|
99
124
|
},
|
|
100
125
|
"devDependencies": {
|
|
101
126
|
"@types/node": "^22.10.1",
|
|
102
127
|
"@typescript-eslint/eslint-plugin": "^8.38.0",
|
|
103
128
|
"@typescript-eslint/parser": "^8.38.0",
|
|
104
129
|
"eslint": "^9.31.0",
|
|
105
|
-
"
|
|
130
|
+
"glob": "^11.0.3",
|
|
131
|
+
"jest": "^29.7.0",
|
|
132
|
+
"typescript": "^5.7.2"
|
|
106
133
|
},
|
|
107
134
|
"bugs": {
|
|
108
135
|
"url": "https://github.com/sun-asterisk/engineer-excellence/issues"
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const rule = require('./plugin/rules/common/c041-no-config-inline.js');
|
|
4
|
-
|
|
5
|
-
// Simple test cases
|
|
6
|
-
const testCases = [
|
|
7
|
-
// Should NOT flag (false positives to avoid)
|
|
8
|
-
{ code: 'const inputType = "password";', shouldError: false, desc: 'InputType definition' },
|
|
9
|
-
{ code: 'const fieldType = "password";', shouldError: false, desc: 'Field type' },
|
|
10
|
-
{ code: 'const usePassword = "password_validation";', shouldError: false, desc: 'Hook usage' },
|
|
11
|
-
{ code: 'const testPassword = "test123";', shouldError: false, desc: 'Test data' },
|
|
12
|
-
{ code: 'const mockSecret = "mock_secret_key";', shouldError: false, desc: 'Mock data' },
|
|
13
|
-
{ code: 'const route = "/reset_password";', shouldError: false, desc: 'Route path' },
|
|
14
|
-
|
|
15
|
-
// SHOULD flag (real sensitive data)
|
|
16
|
-
{ code: 'const dbPassword = "MySecretP@ssw0rd123";', shouldError: true, desc: 'Real hardcoded password' },
|
|
17
|
-
{ code: 'const apiKey = "sk-1234567890abcdef1234567890abcdef";', shouldError: true, desc: 'Real API key' },
|
|
18
|
-
{ code: 'const authToken = "bearer_token_12345678901234567";', shouldError: true, desc: 'Real auth token' }
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
console.log('🧪 Testing ESLint C041 rule manually...\n');
|
|
22
|
-
|
|
23
|
-
let passed = 0;
|
|
24
|
-
let failed = 0;
|
|
25
|
-
|
|
26
|
-
// Mock context for testing
|
|
27
|
-
function createMockContext(code) {
|
|
28
|
-
return {
|
|
29
|
-
getSourceCode: () => ({
|
|
30
|
-
lines: code.split('\n')
|
|
31
|
-
}),
|
|
32
|
-
report: ({ node, messageId }) => {
|
|
33
|
-
return { reported: true, messageId };
|
|
34
|
-
}
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
testCases.forEach((testCase, index) => {
|
|
39
|
-
try {
|
|
40
|
-
let reported = false;
|
|
41
|
-
|
|
42
|
-
// Parse the code to extract literal nodes (simplified)
|
|
43
|
-
const literalMatch = testCase.code.match(/"([^"]+)"/);
|
|
44
|
-
if (literalMatch) {
|
|
45
|
-
const mockNode = {
|
|
46
|
-
value: literalMatch[1],
|
|
47
|
-
loc: { start: { line: 1 } }
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const mockContext = {
|
|
51
|
-
getSourceCode: () => ({
|
|
52
|
-
lines: [testCase.code]
|
|
53
|
-
}),
|
|
54
|
-
report: ({ node, messageId }) => {
|
|
55
|
-
reported = true;
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const ruleImplementation = rule.create(mockContext);
|
|
60
|
-
ruleImplementation.Literal(mockNode);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const testPassed = reported === testCase.shouldError;
|
|
64
|
-
|
|
65
|
-
if (testPassed) {
|
|
66
|
-
console.log(`✅ Test ${index + 1}: ${testCase.desc}`);
|
|
67
|
-
passed++;
|
|
68
|
-
} else {
|
|
69
|
-
console.log(`❌ Test ${index + 1}: ${testCase.desc}`);
|
|
70
|
-
console.log(` Expected: ${testCase.shouldError ? 'Should flag' : 'Should NOT flag'}`);
|
|
71
|
-
console.log(` Actual: ${reported ? 'Flagged' : 'Not flagged'}`);
|
|
72
|
-
failed++;
|
|
73
|
-
}
|
|
74
|
-
} catch (error) {
|
|
75
|
-
console.log(`💥 Test ${index + 1}: ${testCase.desc} - Error: ${error.message}`);
|
|
76
|
-
failed++;
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
console.log(`\n📊 Test Results: ${passed} passed, ${failed} failed`);
|
|
81
|
-
console.log(`Success rate: ${Math.round((passed / (passed + failed)) * 100)}%`);
|
|
82
|
-
|
|
83
|
-
if (failed === 0) {
|
|
84
|
-
console.log('\n🎉 All ESLint rule tests passed!');
|
|
85
|
-
} else {
|
|
86
|
-
console.log('\n⚠️ Some tests failed. Review the rule logic.');
|
|
87
|
-
}
|