renderpilot 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +121 -0
- package/package.json +65 -0
- package/src/cli/commands/scan.js +83 -0
- package/src/cli/index.js +49 -0
- package/src/cli/options.js +18 -0
- package/src/config/ConfigLoader.js +102 -0
- package/src/config/DefaultConfig.js +33 -0
- package/src/index.js +34 -0
- package/src/parser/ASTUtils.js +241 -0
- package/src/parser/BabelParser.js +66 -0
- package/src/parser/ComponentDetector.js +53 -0
- package/src/reporters/HtmlReporter.js +42 -0
- package/src/reporters/JsonReporter.js +52 -0
- package/src/reporters/TerminalReporter.js +91 -0
- package/src/reporters/templates/report.html.js +655 -0
- package/src/rules/base/AbstractRule.js +136 -0
- package/src/rules/engine/RuleEngine.js +67 -0
- package/src/rules/implementations/R001_MissingKeyProp.js +97 -0
- package/src/rules/implementations/R002_InfiniteUseEffect.js +86 -0
- package/src/rules/implementations/R003_IncorrectDepsArray.js +112 -0
- package/src/rules/implementations/R004_LargeComponent.js +47 -0
- package/src/rules/implementations/R005_InlineCallback.js +58 -0
- package/src/rules/implementations/R006_HeavyRenderCalc.js +86 -0
- package/src/rules/implementations/R007_MemoCandidate.js +87 -0
- package/src/rules/implementations/R008_NestedComponent.js +65 -0
- package/src/rules/implementations/R009_UnusedState.js +62 -0
- package/src/rules/index.js +37 -0
- package/src/rules/registry/RuleRegistry.js +67 -0
- package/src/scanner/FileScanner.js +42 -0
- package/src/scanner/ProjectScanner.js +116 -0
- package/src/score/CategoryScorer.js +53 -0
- package/src/score/ScoreCalculator.js +97 -0
- package/src/score/ScoreWeights.js +33 -0
- package/src/types/index.js +176 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# RenderPilot ✈️
|
|
2
|
+
|
|
3
|
+
**The Intelligent React Performance Analysis CLI**
|
|
4
|
+
|
|
5
|
+
RenderPilot statically analyzes your React (**JS/TS/JSX/TSX**) codebase to detect performance anti-patterns, explain why they occur, estimate their impact, and suggest pragmatic fixes. It generates a comprehensive **Performance Score** and provides a developer-friendly interactive dashboard to track your fixes.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 🌟 Key Features
|
|
10
|
+
|
|
11
|
+
- **Built for Modern Stacks:** Seamlessly supports TypeScript (`.ts`, `.tsx`) and modern JavaScript syntax out of the box using Babel. No complex configuration required.
|
|
12
|
+
- **Smart Component Detection:** Uses an advanced AST (Abstract Syntax Tree) engine to identify React Component boundaries. This means it safely ignores backend files (Express, NestJS), utility functions, and non-React files—preventing false positives.
|
|
13
|
+
- **Interactive Developer Dashboard:** Generates a stunning, Vercel-inspired HTML report with collapsible file accordions, line-number highlighting, and a **"Mark as Fixed"** workflow to easily track your debugging progress.
|
|
14
|
+
- **Pragmatic, Senior-Level Advice:** RenderPilot is designed to be a Performance Advisor, not a dogmatic linter.
|
|
15
|
+
- Differentiates between array mutations (`.sort()`) vs pure array methods (`.filter()`).
|
|
16
|
+
- Only recommends `React.memo` for components that actually receive props.
|
|
17
|
+
- **Performance Grading:** Get an intelligent overall grade (A-F) based on severity-weighted categories (Rendering, Hooks, Architecture, Maintainability).
|
|
18
|
+
|
|
19
|
+
## 🚀 Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g renderpilot
|
|
23
|
+
# or
|
|
24
|
+
npx renderpilot scan .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 🛠️ Usage
|
|
28
|
+
|
|
29
|
+
Scan your current directory (Outputs to Terminal):
|
|
30
|
+
```bash
|
|
31
|
+
renderpilot scan .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Recommended:** Generate the interactive HTML dashboard:
|
|
35
|
+
```bash
|
|
36
|
+
renderpilot report .
|
|
37
|
+
# Equivalent to: renderpilot scan . --html
|
|
38
|
+
```
|
|
39
|
+
*(This will automatically open `renderpilot-report.html` in your default browser).*
|
|
40
|
+
|
|
41
|
+
Generate a JSON payload for CI integration:
|
|
42
|
+
```bash
|
|
43
|
+
renderpilot scan . --json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Get just the score (useful for CI/CD pipeline gates):
|
|
47
|
+
```bash
|
|
48
|
+
renderpilot scan . --score
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## ⚙️ Configuration
|
|
52
|
+
|
|
53
|
+
RenderPilot works zero-config out of the box. To customize it, create a `renderpilot.config.js` file in your project root:
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
module.exports = {
|
|
57
|
+
// Directories to ignore during scan
|
|
58
|
+
ignoreDirs: ['node_modules', 'dist', 'build', '.next'],
|
|
59
|
+
|
|
60
|
+
// Maximum component lines before triggering a warning
|
|
61
|
+
maxComponentLines: 300,
|
|
62
|
+
|
|
63
|
+
// Override severity for specific rules
|
|
64
|
+
severityOverrides: {
|
|
65
|
+
'R001': 'critical', // Missing key prop
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
// Explicitly disable certain rules
|
|
69
|
+
disabledRules: ['R004', 'R007'],
|
|
70
|
+
|
|
71
|
+
// Output directory for HTML and JSON reports
|
|
72
|
+
outputDir: '.renderpilot'
|
|
73
|
+
};
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 📏 Core Rules
|
|
77
|
+
|
|
78
|
+
| ID | Name | Category | Severity |
|
|
79
|
+
|----|------|----------|----------|
|
|
80
|
+
| **R001** | Missing Key Prop | Rendering | Warning |
|
|
81
|
+
| **R002** | Infinite useEffect Loop | Hooks | Critical |
|
|
82
|
+
| **R003** | Incorrect Dependency Array | Hooks | Warning |
|
|
83
|
+
| **R004** | Large Component | Architecture | Info |
|
|
84
|
+
| **R005** | Inline Callback Detection | Rendering | Info |
|
|
85
|
+
| **R006** | Heavy Calculations in Render | Rendering | Warning |
|
|
86
|
+
| **R007** | React.memo Candidate | Rendering | Info |
|
|
87
|
+
| **R008** | Nested Component Declarations | Architecture | Critical |
|
|
88
|
+
| **R009** | Unused React State | Maintainability| Warning |
|
|
89
|
+
|
|
90
|
+
## 💯 How the Scoring Works
|
|
91
|
+
|
|
92
|
+
Your project starts with 100 points. Points are deducted based on rule violations, severity, and category weight.
|
|
93
|
+
- **Critical Issues (-15 points)** (Multiplied by category weight)
|
|
94
|
+
- **Warnings (-5 points)**
|
|
95
|
+
- **Info (-1 point)**
|
|
96
|
+
|
|
97
|
+
Categories are weighted by performance impact:
|
|
98
|
+
- **Rendering** (1.5x)
|
|
99
|
+
- **Hooks** (1.3x)
|
|
100
|
+
- **Architecture** (1.0x)
|
|
101
|
+
- **Maintainability** (0.7x)
|
|
102
|
+
|
|
103
|
+
## 💻 Programmatic API
|
|
104
|
+
|
|
105
|
+
You can use RenderPilot as a Node.js library to integrate it into your own tools:
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
const { ProjectScanner, ScoreCalculator, loadConfig } = require('renderpilot');
|
|
109
|
+
|
|
110
|
+
async function run() {
|
|
111
|
+
const config = loadConfig('./src');
|
|
112
|
+
const scanResult = await ProjectScanner.scan('./src', config);
|
|
113
|
+
const score = ScoreCalculator.compute(scanResult.allViolations);
|
|
114
|
+
|
|
115
|
+
console.log('Score:', score.overall);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## 📄 License
|
|
120
|
+
|
|
121
|
+
MIT © RenderPilot Contributors
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "renderpilot",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The Intelligent React (JS/TSX) Performance Analysis CLI — detects anti-patterns, generates interactive HTML reports, and provides pragmatic fixes.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"performance",
|
|
8
|
+
"cli",
|
|
9
|
+
"static-analysis",
|
|
10
|
+
"ast",
|
|
11
|
+
"jsx",
|
|
12
|
+
"tsx",
|
|
13
|
+
"react-performance",
|
|
14
|
+
"code-analysis",
|
|
15
|
+
"developer-tools"
|
|
16
|
+
],
|
|
17
|
+
"author": "RenderPilot Contributors",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"homepage": "https://github.com/ADITYA-user18/renderpilot#readme",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/ADITYA-user18/renderpilot.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/ADITYA-user18/renderpilot/issues"
|
|
26
|
+
},
|
|
27
|
+
"main": "./src/index.js",
|
|
28
|
+
"bin": {
|
|
29
|
+
"renderpilot": "./src/cli/index.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"src",
|
|
33
|
+
"README.md",
|
|
34
|
+
"CHANGELOG.md"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"start": "node src/cli/index.js",
|
|
41
|
+
"dev": "node src/cli/index.js",
|
|
42
|
+
"test": "jest",
|
|
43
|
+
"test:watch": "jest --watch",
|
|
44
|
+
"test:coverage": "jest --coverage",
|
|
45
|
+
"lint": "eslint src",
|
|
46
|
+
"lint:fix": "eslint src --fix",
|
|
47
|
+
"format": "prettier --write \"src/**/*.js\""
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"@babel/parser": "^7.24.0",
|
|
51
|
+
"@babel/traverse": "^7.24.0",
|
|
52
|
+
"@babel/types": "^7.24.0",
|
|
53
|
+
"chalk": "^4.1.2",
|
|
54
|
+
"commander": "^12.1.0",
|
|
55
|
+
"fast-glob": "^3.3.2",
|
|
56
|
+
"open": "^8.4.2",
|
|
57
|
+
"ora": "^5.4.1",
|
|
58
|
+
"zod": "^3.23.8"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"jest": "^29.7.0",
|
|
62
|
+
"prettier": "^3.3.2"
|
|
63
|
+
},
|
|
64
|
+
"type": "commonjs"
|
|
65
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file cli/commands/scan.js
|
|
3
|
+
* @description
|
|
4
|
+
* The main 'scan' command.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const ora = require('ora');
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const open = require('open');
|
|
13
|
+
const { loadConfig } = require('../../config/ConfigLoader');
|
|
14
|
+
const { ProjectScanner } = require('../../scanner/ProjectScanner');
|
|
15
|
+
const { ScoreCalculator } = require('../../score/ScoreCalculator');
|
|
16
|
+
const { TerminalReporter } = require('../../reporters/TerminalReporter');
|
|
17
|
+
const { JsonReporter } = require('../../reporters/JsonReporter');
|
|
18
|
+
const { HtmlReporter } = require('../../reporters/HtmlReporter');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handle the 'scan' command.
|
|
22
|
+
* @param {string} targetDir
|
|
23
|
+
* @param {object} cmdOptions
|
|
24
|
+
*/
|
|
25
|
+
async function handleScan(targetDir = '.', cmdOptions) {
|
|
26
|
+
const rootDir = path.resolve(process.cwd(), targetDir);
|
|
27
|
+
|
|
28
|
+
// 1. Load config
|
|
29
|
+
const config = loadConfig(rootDir); // In the future, cmdOptions.config can override
|
|
30
|
+
|
|
31
|
+
// 2. Initialize Spinner
|
|
32
|
+
const spinner = ora(`Discovering React files in ${chalk.bold(rootDir)}...`).start();
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
// 3. Run Scan
|
|
36
|
+
const scanResult = await ProjectScanner.scan(rootDir, config);
|
|
37
|
+
|
|
38
|
+
spinner.succeed(`Scanned ${scanResult.totalFiles} files in ${scanResult.durationMs}ms`);
|
|
39
|
+
|
|
40
|
+
// 4. Calculate Score
|
|
41
|
+
const score = ScoreCalculator.compute(scanResult.allViolations);
|
|
42
|
+
|
|
43
|
+
const reportData = {
|
|
44
|
+
version: require('../../../package.json').version,
|
|
45
|
+
scan: scanResult,
|
|
46
|
+
score,
|
|
47
|
+
config,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// 5. Generate Outputs
|
|
51
|
+
if (cmdOptions.score) {
|
|
52
|
+
// Just print the score
|
|
53
|
+
console.log(`\n🏆 Performance Score: ${chalk.bold.green(score.overall + '/100')} (${score.grade})`);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
TerminalReporter.generate(reportData);
|
|
58
|
+
|
|
59
|
+
if (cmdOptions.json) {
|
|
60
|
+
const jsonPath = JsonReporter.generate(reportData);
|
|
61
|
+
console.log(`💾 JSON report saved to: ${chalk.blue(jsonPath)}`);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (cmdOptions.html) {
|
|
65
|
+
const htmlPath = HtmlReporter.generate(reportData);
|
|
66
|
+
console.log(`🌐 HTML report saved to: ${chalk.blue(htmlPath)}`);
|
|
67
|
+
|
|
68
|
+
// Auto-open if possible
|
|
69
|
+
try {
|
|
70
|
+
await open(htmlPath);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.log(chalk.dim(`(Could not auto-open browser: ${err.message})`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
} catch (error) {
|
|
77
|
+
spinner.fail('Scan failed due to an internal error.');
|
|
78
|
+
console.error(chalk.red('\nError Details:'), error);
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
module.exports = { handleScan };
|
package/src/cli/index.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file cli/index.js
|
|
5
|
+
* @description
|
|
6
|
+
* Main CLI entry point. Wires up Commander and parses arguments.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const { Command } = require('commander');
|
|
12
|
+
const { version, description } = require('../../package.json');
|
|
13
|
+
const { handleScan } = require('./commands/scan');
|
|
14
|
+
const { options } = require('./options');
|
|
15
|
+
|
|
16
|
+
// Ensure rules are registered before anything runs
|
|
17
|
+
require('../rules/index');
|
|
18
|
+
|
|
19
|
+
const program = new Command();
|
|
20
|
+
|
|
21
|
+
program
|
|
22
|
+
.name('renderpilot')
|
|
23
|
+
.description(description)
|
|
24
|
+
.version(version);
|
|
25
|
+
|
|
26
|
+
program
|
|
27
|
+
.command('scan [directory]')
|
|
28
|
+
.description('Scan a React project for performance issues')
|
|
29
|
+
.addOption(options.json)
|
|
30
|
+
.addOption(options.html)
|
|
31
|
+
.addOption(options.score)
|
|
32
|
+
.addOption(options.config)
|
|
33
|
+
.action((directory, cmdOptions) => {
|
|
34
|
+
handleScan(directory, cmdOptions);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// renderpilot report is just an alias for scan --html for now
|
|
38
|
+
program
|
|
39
|
+
.command('report [directory]')
|
|
40
|
+
.description('Scan a project and immediately open the HTML report')
|
|
41
|
+
.action((directory) => {
|
|
42
|
+
handleScan(directory, { html: true });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
program.parse(process.argv);
|
|
46
|
+
|
|
47
|
+
if (!process.argv.slice(2).length) {
|
|
48
|
+
program.outputHelp();
|
|
49
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file cli/options.js
|
|
3
|
+
* @description
|
|
4
|
+
* Reusable Commander.js options shared across multiple commands.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { Option } = require('commander');
|
|
10
|
+
|
|
11
|
+
const options = {
|
|
12
|
+
json: new Option('--json', 'Output results as a JSON file').default(false),
|
|
13
|
+
html: new Option('--html', 'Output results as an interactive HTML report').default(false),
|
|
14
|
+
score: new Option('--score', 'Only print the overall performance score').default(false),
|
|
15
|
+
config: new Option('-c, --config <path>', 'Path to custom config file'),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = { options };
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file config/ConfigLoader.js
|
|
3
|
+
* @description
|
|
4
|
+
* Loads and merges user configuration from renderpilot.config.js
|
|
5
|
+
* with built-in defaults. Performs validation and normalization.
|
|
6
|
+
*
|
|
7
|
+
* Resolution order:
|
|
8
|
+
* 1. Look for renderpilot.config.js in the scan root
|
|
9
|
+
* 2. Look for renderpilot.config.js in process.cwd()
|
|
10
|
+
* 3. Fall back to DEFAULT_CONFIG
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const { DEFAULT_CONFIG } = require('./DefaultConfig');
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Attempts to load a renderpilot.config.js file from the given directory.
|
|
21
|
+
* @param {string} dir - Directory to search in
|
|
22
|
+
* @returns {import('../types').UserConfig|null} User config or null if not found
|
|
23
|
+
*/
|
|
24
|
+
function loadConfigFile(dir) {
|
|
25
|
+
const configPath = path.join(dir, 'renderpilot.config.js');
|
|
26
|
+
if (!fs.existsSync(configPath)) return null;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Clear require cache to support hot-reloading in watch mode
|
|
30
|
+
delete require.cache[require.resolve(configPath)];
|
|
31
|
+
return require(configPath);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
console.warn(
|
|
34
|
+
`[RenderPilot] Warning: Failed to load config at ${configPath}: ${err.message}`
|
|
35
|
+
);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Merges user config with defaults and normalizes into a ResolvedConfig.
|
|
42
|
+
* @param {import('../types').UserConfig} userConfig - User-provided config
|
|
43
|
+
* @returns {import('../types').ResolvedConfig} Resolved config
|
|
44
|
+
*/
|
|
45
|
+
function mergeConfig(userConfig) {
|
|
46
|
+
const merged = { ...DEFAULT_CONFIG };
|
|
47
|
+
|
|
48
|
+
if (Array.isArray(userConfig.ignoreDirs)) {
|
|
49
|
+
// Merge user ignore dirs with defaults (union)
|
|
50
|
+
merged.ignoreDirs = [
|
|
51
|
+
...new Set([...DEFAULT_CONFIG.ignoreDirs, ...userConfig.ignoreDirs]),
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeof userConfig.maxComponentLines === 'number' && userConfig.maxComponentLines > 0) {
|
|
56
|
+
merged.maxComponentLines = userConfig.maxComponentLines;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (userConfig.severityOverrides && typeof userConfig.severityOverrides === 'object') {
|
|
60
|
+
merged.severityOverrides = { ...userConfig.severityOverrides };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (Array.isArray(userConfig.enabledRules) && userConfig.enabledRules.length > 0) {
|
|
64
|
+
merged.enabledRules = new Set(userConfig.enabledRules);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (Array.isArray(userConfig.disabledRules) && userConfig.disabledRules.length > 0) {
|
|
68
|
+
merged.disabledRules = new Set(userConfig.disabledRules);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (typeof userConfig.outputDir === 'string' && userConfig.outputDir.trim()) {
|
|
72
|
+
merged.outputDir = userConfig.outputDir.trim();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
merged.hasCustomConfig = true;
|
|
76
|
+
return merged;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Loads and resolves configuration for a scan.
|
|
81
|
+
* Searches scanRoot first, then cwd, then uses defaults.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} scanRoot - The root directory being scanned
|
|
84
|
+
* @returns {import('../types').ResolvedConfig} Fully resolved config
|
|
85
|
+
*/
|
|
86
|
+
function loadConfig(scanRoot) {
|
|
87
|
+
// Try scan root first
|
|
88
|
+
let userConfig = loadConfigFile(scanRoot);
|
|
89
|
+
|
|
90
|
+
// Fall back to cwd if not in scan root
|
|
91
|
+
if (!userConfig && scanRoot !== process.cwd()) {
|
|
92
|
+
userConfig = loadConfigFile(process.cwd());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!userConfig) {
|
|
96
|
+
return { ...DEFAULT_CONFIG };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return mergeConfig(userConfig);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = { loadConfig };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file config/DefaultConfig.js
|
|
3
|
+
* @description Built-in default configuration for RenderPilot.
|
|
4
|
+
* All defaults live here — never scattered across modules.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
/** @type {import('../types').ResolvedConfig} */
|
|
10
|
+
const DEFAULT_CONFIG = {
|
|
11
|
+
ignoreDirs: [
|
|
12
|
+
'node_modules',
|
|
13
|
+
'dist',
|
|
14
|
+
'build',
|
|
15
|
+
'coverage',
|
|
16
|
+
'.git',
|
|
17
|
+
'.next',
|
|
18
|
+
'.nuxt',
|
|
19
|
+
'out',
|
|
20
|
+
'.cache',
|
|
21
|
+
'public',
|
|
22
|
+
'static',
|
|
23
|
+
'assets',
|
|
24
|
+
],
|
|
25
|
+
maxComponentLines: 300,
|
|
26
|
+
severityOverrides: {},
|
|
27
|
+
enabledRules: null, // null = all rules enabled
|
|
28
|
+
disabledRules: new Set(),
|
|
29
|
+
outputDir: '.renderpilot',
|
|
30
|
+
hasCustomConfig: false,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = { DEFAULT_CONFIG };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file index.js
|
|
3
|
+
* @description
|
|
4
|
+
* Main package export. Allows RenderPilot to be consumed programmatically
|
|
5
|
+
* by other Node.js scripts instead of just via the CLI.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
// Ensure rules are registered
|
|
11
|
+
require('./rules/index');
|
|
12
|
+
|
|
13
|
+
const { ProjectScanner } = require('./scanner/ProjectScanner');
|
|
14
|
+
const { ScoreCalculator } = require('./score/ScoreCalculator');
|
|
15
|
+
const { loadConfig } = require('./config/ConfigLoader');
|
|
16
|
+
const { ruleRegistry } = require('./rules/registry/RuleRegistry');
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
// Core APIs
|
|
20
|
+
ProjectScanner,
|
|
21
|
+
ScoreCalculator,
|
|
22
|
+
loadConfig,
|
|
23
|
+
|
|
24
|
+
// Rule Registry access for custom rules
|
|
25
|
+
ruleRegistry,
|
|
26
|
+
|
|
27
|
+
// Re-export base rule for custom rule creation
|
|
28
|
+
AbstractRule: require('./rules/base/AbstractRule').AbstractRule,
|
|
29
|
+
|
|
30
|
+
// Reporters
|
|
31
|
+
TerminalReporter: require('./reporters/TerminalReporter').TerminalReporter,
|
|
32
|
+
JsonReporter: require('./reporters/JsonReporter').JsonReporter,
|
|
33
|
+
HtmlReporter: require('./reporters/HtmlReporter').HtmlReporter,
|
|
34
|
+
};
|