headhunt 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/LICENSE +24 -0
- package/README.md +258 -0
- package/headhunt.js +10 -0
- package/package.json +36 -0
- package/src/cli.js +114 -0
- package/src/colors.js +50 -0
- package/src/constants.js +31 -0
- package/src/logger.js +10 -0
- package/src/reporter.js +287 -0
- package/src/scraper.js +815 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 imharris24
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software for non-commercial purposes only, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, and distribute
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
1. The Software may not be used for commercial purposes, including but not limited to selling the Software or using it in a commercial product or service.
|
|
13
|
+
2. The Software may not be sublicensed for commercial purposes.
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
# HeadHunt-CLI 🎯
|
|
2
|
+
|
|
3
|
+
> The terminal-first SEO metadata hunter. Scrape, score, and audit any website's SEO health directly from the command line.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/headhunt)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
[](https://nodejs.org/)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ✨ Features
|
|
12
|
+
|
|
13
|
+
### 🔍 Comprehensive SEO Extraction
|
|
14
|
+
- **Basic Metadata** — title, meta description, canonical, robots, viewport, charset, language
|
|
15
|
+
- **Open Graph** — full og: tag detection for social sharing
|
|
16
|
+
- **Twitter Cards** — twitter: card, title, description, image
|
|
17
|
+
- **Schema.org** — JSON-LD & Microdata parsing with type detection
|
|
18
|
+
- **Technical SEO** — doctype, hreflang, pagination, sitemap, RSS, AMP, manifest
|
|
19
|
+
|
|
20
|
+
### 📊 Intelligent Scoring Engine
|
|
21
|
+
- **100-point SEO score** across 14 weighted categories
|
|
22
|
+
- **Letter grade** (A+ to F) with contextual summary
|
|
23
|
+
- **Priority-ranked recommendations** with impact & effort estimates
|
|
24
|
+
|
|
25
|
+
### 🔗 Link & Content Analysis
|
|
26
|
+
- Internal vs external link classification
|
|
27
|
+
- Nofollow, sponsored, UGC rel attribute detection
|
|
28
|
+
- Empty anchor text detection
|
|
29
|
+
- Heading hierarchy validation (H1–H6) with skip detection
|
|
30
|
+
- Image alt-text coverage & lazy-loading audit
|
|
31
|
+
- Word count, paragraph count & keyword density estimation
|
|
32
|
+
|
|
33
|
+
### 🛡️ Security & Performance Signals
|
|
34
|
+
- HTTPS, HSTS, CSP, X-Frame-Options, X-Content-Type-Options
|
|
35
|
+
- Render-blocking script detection
|
|
36
|
+
- HTML payload size & fetch time analysis
|
|
37
|
+
- Estimated total page weight
|
|
38
|
+
|
|
39
|
+
### 📈 Professional Output Modes
|
|
40
|
+
- **Terminal Report** — beautifully formatted with color-coded scores
|
|
41
|
+
- **JSON Export** — full structured data for pipelines (`--json`, `--json-file`)
|
|
42
|
+
- **Score-Only** — CI-friendly single-line output (`--score-only`)
|
|
43
|
+
- **Deep Audit** — verbose recommendations with impact/effort badges (`--audit`)
|
|
44
|
+
- **Side-by-Side Comparison** — compare two URLs (`--compare`)
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 🚀 Installation
|
|
49
|
+
|
|
50
|
+
### Via npm (recommended)
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install -g headhunt
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Then run from anywhere:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
headhunt https://example.com
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Via npx (no install)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npx headhunt https://example.com
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### From Source
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/imharris24/HeadHunt.git
|
|
72
|
+
cd HeadHunt
|
|
73
|
+
npm install
|
|
74
|
+
chmod +x headhunt.js
|
|
75
|
+
node headhunt.js https://example.com
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## 📖 Usage
|
|
81
|
+
|
|
82
|
+
### Basic Scan
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
headhunt https://example.com
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Output Modes
|
|
89
|
+
|
|
90
|
+
| Flag | Description |
|
|
91
|
+
|------|-------------|
|
|
92
|
+
| `--json` | Print full raw JSON to stdout |
|
|
93
|
+
| `--json-file` | Save JSON report to `seo-report-<hostname>-<timestamp>.json` |
|
|
94
|
+
| `--score-only` | Print `Score: 78/100 Grade: B+` (CI-friendly) |
|
|
95
|
+
| `--audit` | Show full recommendations with impact & effort badges |
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# CI pipeline check
|
|
99
|
+
headhunt --score-only https://example.com
|
|
100
|
+
|
|
101
|
+
# Deep audit with actionable fixes
|
|
102
|
+
headhunt --audit https://example.com
|
|
103
|
+
|
|
104
|
+
# Export full JSON for further processing
|
|
105
|
+
headhunt --json-file https://example.com
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Skip Analysis (Faster)
|
|
109
|
+
|
|
110
|
+
| Flag | Description |
|
|
111
|
+
|------|-------------|
|
|
112
|
+
| `--no-links` | Skip link analysis |
|
|
113
|
+
| `--no-images` | Skip image analysis |
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
headhunt --no-links --no-images https://example.com
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Compare Two URLs
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
headhunt https://site-a.com --compare https://site-b.com
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Generates a side-by-side comparison table with a declared winner.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## 📋 Example Output
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
════════════════════════════════════════════════════════════════════
|
|
133
|
+
|
|
134
|
+
TARGET https://example.com
|
|
135
|
+
SCANNED 5/23/2026, 10:30:00 AM
|
|
136
|
+
PLATFORM WordPress
|
|
137
|
+
|
|
138
|
+
────────────────────────────────────────────────────────────────────
|
|
139
|
+
|
|
140
|
+
OVERALL SEO SCORE
|
|
141
|
+
|
|
142
|
+
██████████████████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░ 78/100 Grade: B+
|
|
143
|
+
|
|
144
|
+
Solid SEO signals present. Primary opportunities in Image Optimization
|
|
145
|
+
and Structured Data — addressing these could meaningfully boost rankings.
|
|
146
|
+
|
|
147
|
+
────────────────────────────────────────────────────────────────────
|
|
148
|
+
|
|
149
|
+
CATEGORY BREAKDOWN
|
|
150
|
+
|
|
151
|
+
✓ Title Tag ████████████████░░░░░░ 15/15
|
|
152
|
+
✓ Meta Description ████████████░░░░░░░░░░ 10/12
|
|
153
|
+
~ Heading Structure ████████░░░░░░░░░░░░░░ 6/10
|
|
154
|
+
✓ Canonical URL █████░░░░░░░░░░░░░░░░░ 5/5
|
|
155
|
+
...
|
|
156
|
+
|
|
157
|
+
QUICK STATS
|
|
158
|
+
|
|
159
|
+
Title "Example Domain — A pla..." (47c)
|
|
160
|
+
Meta Description 155 chars
|
|
161
|
+
Canonical Present
|
|
162
|
+
H1 Tags 1 (ideal)
|
|
163
|
+
Word Count ~1,240 words (7 min read)
|
|
164
|
+
Images 12 total, 67% have alt
|
|
165
|
+
HTTPS Yes
|
|
166
|
+
Response Time 420ms
|
|
167
|
+
|
|
168
|
+
RECOMMENDATIONS (7 items)
|
|
169
|
+
|
|
170
|
+
01. ● HIGH Image Optimization
|
|
171
|
+
Issue: 4 image(s) missing alt text (33% uncovered).
|
|
172
|
+
Fix: Add descriptive alt attributes to all meaningful images...
|
|
173
|
+
|
|
174
|
+
02. ● MEDIUM Structured Data
|
|
175
|
+
Issue: No structured data (schema.org) found.
|
|
176
|
+
Fix: Implement JSON-LD structured data appropriate to your content type...
|
|
177
|
+
|
|
178
|
+
════════════════════════════════════════════════════════════════════
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 🏗️ Architecture
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
HeadHuntSEO
|
|
187
|
+
├── Fetch Engine → axios with custom headers & redirect handling
|
|
188
|
+
├── Parser Layer → cheerio for fast server-side HTML parsing
|
|
189
|
+
├── Extractors → modular metadata, schema, link, image, content
|
|
190
|
+
├── Scoring Engine → weighted rubric across 14 SEO categories
|
|
191
|
+
├── Recommendations → priority-ranked, impact/effort tagged advice
|
|
192
|
+
└── Display Engine → ANSI-colored terminal reports & JSON serializers
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## 🔧 Development
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Clone & setup
|
|
201
|
+
git clone https://github.com/imharris24/HeadHunt.git
|
|
202
|
+
cd HeadHunt
|
|
203
|
+
npm install
|
|
204
|
+
|
|
205
|
+
# Run locally
|
|
206
|
+
node headhunt.js https://example.com --audit
|
|
207
|
+
|
|
208
|
+
# Run tests
|
|
209
|
+
npm test
|
|
210
|
+
|
|
211
|
+
# Lint
|
|
212
|
+
npm run lint
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## 🗺️ Roadmap
|
|
218
|
+
|
|
219
|
+
| Feature | Status |
|
|
220
|
+
|---------|--------|
|
|
221
|
+
| Core metadata extraction | ✅ |
|
|
222
|
+
| Intelligent scoring engine | ✅ |
|
|
223
|
+
| Terminal reporting | ✅ |
|
|
224
|
+
| JSON export modes | ✅ |
|
|
225
|
+
| URL comparison | ✅ |
|
|
226
|
+
| npm global install | ✅ |
|
|
227
|
+
| **JavaScript rendering** (Puppeteer/Playwright) | 🚧 |
|
|
228
|
+
| **Bulk URL processing** | 🚧 |
|
|
229
|
+
| **Broken link checking** | 🚧 |
|
|
230
|
+
| **CSV / Markdown export** | 🚧 |
|
|
231
|
+
| **Proxy & bot-protection bypass** | 🚧 |
|
|
232
|
+
| **Core Web Vitals integration** | 🚧 |
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## 🤝 Contributing
|
|
237
|
+
|
|
238
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
239
|
+
|
|
240
|
+
1. Fork the repository
|
|
241
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
242
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
243
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
244
|
+
5. Open a Pull Request
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## 📄 License
|
|
249
|
+
|
|
250
|
+
This project is licensed under the MIT License — see the [LICENSE](LICENSE) file for details.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## 🐛 Support
|
|
255
|
+
|
|
256
|
+
Found a bug or have a feature request? Please [open an issue](https://github.com/imharris24/HeadHunt/issues).
|
|
257
|
+
|
|
258
|
+
Built with 💻 by [imharris24](https://github.com/imharris24)
|
package/headhunt.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "headhunt",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "The terminal-first SEO metadata hunter. Scrape, structure, and visualize website <head> tags in JSON format.",
|
|
5
|
+
"main": "headhunt.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"headhunt": "./headhunt.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "git+https://github.com/imharris24/HeadHunt-CLI.git"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"seo",
|
|
18
|
+
"scraper",
|
|
19
|
+
"cli",
|
|
20
|
+
"metadata",
|
|
21
|
+
"open-graph",
|
|
22
|
+
"seo-audit",
|
|
23
|
+
"headhunt"
|
|
24
|
+
],
|
|
25
|
+
"author": "imharris24",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"type": "commonjs",
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/imharris24/HeadHunt-CLI/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/imharris24/HeadHunt-CLI#readme",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"axios": "^1.13.2",
|
|
34
|
+
"cheerio": "^1.1.2"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const { URL } = require('url');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { fmt } = require('./colors');
|
|
4
|
+
const SEOScraper = require('./scraper');
|
|
5
|
+
const { printBanner, printReport, printComparison } = require('./reporter');
|
|
6
|
+
|
|
7
|
+
async function main() {
|
|
8
|
+
const args = process.argv.slice(2);
|
|
9
|
+
|
|
10
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) {
|
|
11
|
+
printBanner();
|
|
12
|
+
console.log(` ${fmt.bold('USAGE')}`);
|
|
13
|
+
console.log(` headhunt [options] <url>`);
|
|
14
|
+
console.log();
|
|
15
|
+
console.log(` ${fmt.bold('OPTIONS')}`);
|
|
16
|
+
console.log(` ${fmt.cyan('--json')} Output raw JSON to stdout`);
|
|
17
|
+
console.log(` ${fmt.cyan('--json-file')} Save full JSON report to file`);
|
|
18
|
+
console.log(` ${fmt.cyan('--no-links')} Skip link crawl (faster)`);
|
|
19
|
+
console.log(` ${fmt.cyan('--no-images')} Skip image analysis`);
|
|
20
|
+
console.log(` ${fmt.cyan('--score-only')} Print score and grade only`);
|
|
21
|
+
console.log(` ${fmt.cyan('--audit')} Full recommendations with impact/effort`);
|
|
22
|
+
console.log(` ${fmt.cyan('--compare <url>')} Compare two URLs side by side`);
|
|
23
|
+
console.log();
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const options = {
|
|
28
|
+
json: args.includes('--json'),
|
|
29
|
+
jsonFile: args.includes('--json-file'),
|
|
30
|
+
noLinks: args.includes('--no-links'),
|
|
31
|
+
noImages: args.includes('--no-images'),
|
|
32
|
+
scoreOnly: args.includes('--score-only'),
|
|
33
|
+
audit: args.includes('--audit'),
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const compareIdx = args.indexOf('--compare');
|
|
37
|
+
const compareUrl = compareIdx !== -1 ? args[compareIdx + 1] : null;
|
|
38
|
+
|
|
39
|
+
const urls = args.filter(a => !a.startsWith('--') && a !== compareUrl && (a.startsWith('http://') || a.startsWith('https://')));
|
|
40
|
+
|
|
41
|
+
if (urls.length === 0) {
|
|
42
|
+
console.error(fmt.red(' \u2717 No valid URL provided. URLs must start with http:// or https://'));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const targetUrl = urls[0];
|
|
47
|
+
|
|
48
|
+
try { new URL(targetUrl); } catch { console.error(fmt.red(' \u2717 Invalid URL.')); process.exit(1); }
|
|
49
|
+
|
|
50
|
+
if (!options.json && !options.scoreOnly) printBanner();
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
if (!compareUrl) {
|
|
54
|
+
const scraper = new SEOScraper(targetUrl, options);
|
|
55
|
+
if (!options.json && !options.scoreOnly) console.log(` ${fmt.bold('SCANNING...')}\n`);
|
|
56
|
+
await scraper.fetchPage();
|
|
57
|
+
await scraper.analyze();
|
|
58
|
+
const meta = scraper.metadata;
|
|
59
|
+
|
|
60
|
+
if (options.json) {
|
|
61
|
+
console.log(JSON.stringify(meta, null, 2));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (options.scoreOnly) {
|
|
66
|
+
console.log(`Score: ${meta.overallScore}/100 Grade: ${meta.grade}`);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
printReport(meta, options);
|
|
71
|
+
|
|
72
|
+
if (options.jsonFile) {
|
|
73
|
+
const fname = `seo-report-${new URL(targetUrl).hostname}-${Date.now()}.json`;
|
|
74
|
+
fs.writeFileSync(fname, JSON.stringify(meta, null, 2));
|
|
75
|
+
console.log(` ${fmt.green('\u2713')} JSON report saved \u2192 ${fmt.cyan(fname)}`);
|
|
76
|
+
console.log();
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
try { new URL(compareUrl); } catch { console.error(fmt.red(' \u2717 Invalid compare URL.')); process.exit(1); }
|
|
80
|
+
|
|
81
|
+
console.log(` ${fmt.bold('SCANNING A...')}\n`);
|
|
82
|
+
const scraperA = new SEOScraper(targetUrl, options);
|
|
83
|
+
await scraperA.fetchPage();
|
|
84
|
+
await scraperA.analyze();
|
|
85
|
+
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(` ${fmt.bold('SCANNING B...')}\n`);
|
|
88
|
+
const scraperB = new SEOScraper(compareUrl, options);
|
|
89
|
+
await scraperB.fetchPage();
|
|
90
|
+
await scraperB.analyze();
|
|
91
|
+
|
|
92
|
+
console.log();
|
|
93
|
+
printComparison(scraperA.metadata, scraperB.metadata);
|
|
94
|
+
|
|
95
|
+
if (options.jsonFile) {
|
|
96
|
+
const fname = `seo-compare-${Date.now()}.json`;
|
|
97
|
+
fs.writeFileSync(fname, JSON.stringify({ a: scraperA.metadata, b: scraperB.metadata }, null, 2));
|
|
98
|
+
console.log(` ${fmt.green('\u2713')} Comparison JSON saved \u2192 ${fmt.cyan(fname)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
console.error(`\n ${fmt.red('\u2717 Error:')} ${error.message}`);
|
|
103
|
+
if (error.code === 'ECONNREFUSED') console.error(fmt.dim(' Connection refused \u2014 is the server running?'));
|
|
104
|
+
if (error.code === 'ETIMEDOUT') console.error(fmt.dim(' Request timed out \u2014 try again or check network.'));
|
|
105
|
+
if (error.response?.status === 403) console.error(fmt.dim(' 403 Forbidden \u2014 server is blocking automated requests.'));
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (require.main === module) {
|
|
111
|
+
main();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = main;
|
package/src/colors.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const C = {
|
|
2
|
+
reset: '\x1b[0m',
|
|
3
|
+
bold: '\x1b[1m',
|
|
4
|
+
dim: '\x1b[2m',
|
|
5
|
+
italic: '\x1b[3m',
|
|
6
|
+
underline: '\x1b[4m',
|
|
7
|
+
|
|
8
|
+
black: '\x1b[30m',
|
|
9
|
+
red: '\x1b[31m',
|
|
10
|
+
green: '\x1b[32m',
|
|
11
|
+
yellow: '\x1b[33m',
|
|
12
|
+
blue: '\x1b[34m',
|
|
13
|
+
magenta: '\x1b[35m',
|
|
14
|
+
cyan: '\x1b[36m',
|
|
15
|
+
white: '\x1b[37m',
|
|
16
|
+
gray: '\x1b[90m',
|
|
17
|
+
|
|
18
|
+
bgRed: '\x1b[41m',
|
|
19
|
+
bgGreen: '\x1b[42m',
|
|
20
|
+
bgYellow: '\x1b[43m',
|
|
21
|
+
bgBlue: '\x1b[44m',
|
|
22
|
+
bgMagenta: '\x1b[45m',
|
|
23
|
+
bgCyan: '\x1b[46m',
|
|
24
|
+
|
|
25
|
+
brightRed: '\x1b[91m',
|
|
26
|
+
brightGreen: '\x1b[92m',
|
|
27
|
+
brightYellow: '\x1b[93m',
|
|
28
|
+
brightBlue: '\x1b[94m',
|
|
29
|
+
brightMagenta: '\x1b[95m',
|
|
30
|
+
brightCyan: '\x1b[96m',
|
|
31
|
+
brightWhite: '\x1b[97m',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const fmt = {
|
|
35
|
+
bold: (s) => `${C.bold}${s}${C.reset}`,
|
|
36
|
+
dim: (s) => `${C.dim}${s}${C.reset}`,
|
|
37
|
+
green: (s) => `${C.green}${s}${C.reset}`,
|
|
38
|
+
red: (s) => `${C.red}${s}${C.reset}`,
|
|
39
|
+
yellow: (s) => `${C.yellow}${s}${C.reset}`,
|
|
40
|
+
cyan: (s) => `${C.cyan}${s}${C.reset}`,
|
|
41
|
+
magenta: (s) => `${C.magenta}${s}${C.reset}`,
|
|
42
|
+
blue: (s) => `${C.blue}${s}${C.reset}`,
|
|
43
|
+
gray: (s) => `${C.gray}${s}${C.reset}`,
|
|
44
|
+
brightGreen: (s) => `${C.brightGreen}${s}${C.reset}`,
|
|
45
|
+
brightRed: (s) => `${C.brightRed}${s}${C.reset}`,
|
|
46
|
+
brightYellow: (s) => `${C.brightYellow}${s}${C.reset}`,
|
|
47
|
+
brightCyan: (s) => `${C.brightCyan}${s}${C.reset}`,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
module.exports = { C, fmt };
|
package/src/constants.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const https = require('https');
|
|
2
|
+
|
|
3
|
+
const httpsAgent = new https.Agent({ rejectUnauthorized: false });
|
|
4
|
+
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
|
|
5
|
+
|
|
6
|
+
const SCORE_WEIGHTS = {
|
|
7
|
+
title: { max: 15, label: 'Title Tag' },
|
|
8
|
+
description: { max: 12, label: 'Meta Description' },
|
|
9
|
+
headings: { max: 10, label: 'Heading Structure' },
|
|
10
|
+
canonical: { max: 5, label: 'Canonical URL' },
|
|
11
|
+
robots: { max: 3, label: 'Robots Meta' },
|
|
12
|
+
openGraph: { max: 8, label: 'Open Graph Tags' },
|
|
13
|
+
twitterCard: { max: 5, label: 'Twitter Card' },
|
|
14
|
+
schema: { max: 8, label: 'Structured Data' },
|
|
15
|
+
images: { max: 8, label: 'Image Optimization' },
|
|
16
|
+
mobile: { max: 5, label: 'Mobile Readiness' },
|
|
17
|
+
performance: { max: 7, label: 'Page Performance' },
|
|
18
|
+
links: { max: 5, label: 'Link Structure' },
|
|
19
|
+
security: { max: 5, label: 'Security Signals' },
|
|
20
|
+
content: { max: 4, label: 'Content Quality' },
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const PRIORITY = {
|
|
24
|
+
CRITICAL: { label: '\u{1F534} CRITICAL', color: '\x1b[91m' },
|
|
25
|
+
HIGH: { label: '\u{1F7E0} HIGH', color: '\x1b[33m' },
|
|
26
|
+
MEDIUM: { label: '\u{1F7E1} MEDIUM', color: '\x1b[93m' },
|
|
27
|
+
LOW: { label: '\u{1F535} LOW', color: '\x1b[36m' },
|
|
28
|
+
GOOD: { label: '\u2705 PASSED', color: '\x1b[92m' },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
module.exports = { httpsAgent, USER_AGENT, SCORE_WEIGHTS, PRIORITY };
|
package/src/logger.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const { fmt } = require('./colors');
|
|
2
|
+
|
|
3
|
+
const log = {
|
|
4
|
+
info: (msg) => console.log(` ${fmt.blue('\u2139')} ${msg}`),
|
|
5
|
+
ok: (msg) => console.log(` ${fmt.green('\u2713')} ${fmt.dim(msg)}`),
|
|
6
|
+
warn: (msg) => console.log(` ${fmt.yellow('\u26A0')} ${msg}`),
|
|
7
|
+
err: (msg) => console.log(` ${fmt.red('\u2717')} ${msg}`),
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
module.exports = log;
|