@rankcli/agent-runtime 0.0.14 → 0.0.16
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/dist/index.d.mts +77 -393
- package/dist/index.d.ts +77 -393
- package/dist/index.js +417 -6443
- package/dist/index.mjs +296 -6313
- package/package.json +6 -1
- package/scripts/build-deno.ts +0 -134
- package/src/analyzers/core-web-vitals-analyzer.test.ts +0 -236
- package/src/analyzers/core-web-vitals-analyzer.ts +0 -557
- package/src/analyzers/geo-analyzer.test.ts +0 -310
- package/src/analyzers/geo-analyzer.ts +0 -814
- package/src/analyzers/image-optimization-analyzer.test.ts +0 -145
- package/src/analyzers/image-optimization-analyzer.ts +0 -348
- package/src/analyzers/index.ts +0 -233
- package/src/analyzers/internal-linking-analyzer.test.ts +0 -141
- package/src/analyzers/internal-linking-analyzer.ts +0 -419
- package/src/analyzers/mobile-seo-analyzer.test.ts +0 -140
- package/src/analyzers/mobile-seo-analyzer.ts +0 -455
- package/src/analyzers/security-headers-analyzer.test.ts +0 -115
- package/src/analyzers/security-headers-analyzer.ts +0 -318
- package/src/analyzers/structured-data-analyzer.test.ts +0 -210
- package/src/analyzers/structured-data-analyzer.ts +0 -590
- package/src/audit/ai/analyzer.ts +0 -347
- package/src/audit/ai/index.ts +0 -29
- package/src/audit/ai/prompts/content-analysis.ts +0 -271
- package/src/audit/ai/types.ts +0 -179
- package/src/audit/checks/additional-checks.ts +0 -439
- package/src/audit/checks/ai-citation-worthiness.ts +0 -399
- package/src/audit/checks/ai-content-structure.ts +0 -325
- package/src/audit/checks/ai-readiness.ts +0 -339
- package/src/audit/checks/anchor-text.ts +0 -179
- package/src/audit/checks/answer-conciseness.ts +0 -322
- package/src/audit/checks/asset-minification.ts +0 -270
- package/src/audit/checks/bing-optimization.ts +0 -206
- package/src/audit/checks/brand-mention-optimization.ts +0 -349
- package/src/audit/checks/caching-headers.ts +0 -305
- package/src/audit/checks/canonical-advanced.ts +0 -150
- package/src/audit/checks/canonical-domain.ts +0 -196
- package/src/audit/checks/citation-quality.ts +0 -358
- package/src/audit/checks/client-rendering.ts +0 -542
- package/src/audit/checks/color-contrast.ts +0 -342
- package/src/audit/checks/content-freshness.ts +0 -170
- package/src/audit/checks/content-science.ts +0 -589
- package/src/audit/checks/conversion-elements.ts +0 -526
- package/src/audit/checks/crawlability.ts +0 -220
- package/src/audit/checks/directory-listing.ts +0 -172
- package/src/audit/checks/dom-analysis.ts +0 -191
- package/src/audit/checks/dom-size.ts +0 -246
- package/src/audit/checks/duplicate-content.ts +0 -194
- package/src/audit/checks/eeat-signals.ts +0 -990
- package/src/audit/checks/entity-seo.ts +0 -396
- package/src/audit/checks/featured-snippet.ts +0 -473
- package/src/audit/checks/freshness-signals.ts +0 -443
- package/src/audit/checks/funnel-intent.ts +0 -463
- package/src/audit/checks/hreflang.ts +0 -174
- package/src/audit/checks/html-compliance.ts +0 -302
- package/src/audit/checks/image-dimensions.ts +0 -167
- package/src/audit/checks/images.ts +0 -160
- package/src/audit/checks/indexnow.ts +0 -275
- package/src/audit/checks/interactive-tools.ts +0 -475
- package/src/audit/checks/internal-link-graph.ts +0 -436
- package/src/audit/checks/keyword-analysis.ts +0 -239
- package/src/audit/checks/keyword-cannibalization.ts +0 -385
- package/src/audit/checks/keyword-placement.ts +0 -471
- package/src/audit/checks/links.ts +0 -273
- package/src/audit/checks/llms-txt.ts +0 -224
- package/src/audit/checks/local-seo.ts +0 -296
- package/src/audit/checks/mobile.ts +0 -167
- package/src/audit/checks/modern-images.ts +0 -226
- package/src/audit/checks/navboost-signals.ts +0 -395
- package/src/audit/checks/on-page.ts +0 -209
- package/src/audit/checks/page-resources.ts +0 -285
- package/src/audit/checks/pagination.ts +0 -180
- package/src/audit/checks/performance.ts +0 -153
- package/src/audit/checks/platform-presence.ts +0 -580
- package/src/audit/checks/redirect-analysis.ts +0 -153
- package/src/audit/checks/redirect-chain.ts +0 -389
- package/src/audit/checks/resource-hints.ts +0 -420
- package/src/audit/checks/responsive-css.ts +0 -247
- package/src/audit/checks/responsive-images.ts +0 -396
- package/src/audit/checks/review-ecosystem.ts +0 -415
- package/src/audit/checks/robots-validation.ts +0 -373
- package/src/audit/checks/security-headers.ts +0 -188
- package/src/audit/checks/security.ts +0 -144
- package/src/audit/checks/serp-preview.ts +0 -251
- package/src/audit/checks/site-maturity.ts +0 -453
- package/src/audit/checks/social-meta.test.ts +0 -275
- package/src/audit/checks/social-meta.ts +0 -134
- package/src/audit/checks/soft-404.ts +0 -151
- package/src/audit/checks/structured-data.ts +0 -238
- package/src/audit/checks/tech-detection.ts +0 -496
- package/src/audit/checks/topical-clusters.ts +0 -435
- package/src/audit/checks/tracker-bloat.ts +0 -462
- package/src/audit/checks/tracking-verification.test.ts +0 -371
- package/src/audit/checks/tracking-verification.ts +0 -636
- package/src/audit/checks/url-safety.ts +0 -682
- package/src/audit/deno-entry.ts +0 -66
- package/src/audit/discovery/index.ts +0 -15
- package/src/audit/discovery/link-crawler.ts +0 -233
- package/src/audit/discovery/repo-routes.ts +0 -347
- package/src/audit/engine.ts +0 -627
- package/src/audit/fixes/index.ts +0 -209
- package/src/audit/fixes/social-meta-fixes.test.ts +0 -329
- package/src/audit/fixes/social-meta-fixes.ts +0 -463
- package/src/audit/index.ts +0 -74
- package/src/audit/runner.test.ts +0 -299
- package/src/audit/runner.ts +0 -130
- package/src/audit/types.ts +0 -1963
- package/src/content/featured-snippet.ts +0 -367
- package/src/content/generator.test.ts +0 -534
- package/src/content/generator.ts +0 -501
- package/src/content/headline.ts +0 -317
- package/src/content/index.ts +0 -62
- package/src/content/intent.ts +0 -258
- package/src/content/keyword-density.ts +0 -349
- package/src/content/readability.ts +0 -262
- package/src/executor.ts +0 -336
- package/src/fixer/framework-fixes.test.ts +0 -489
- package/src/fixer/framework-fixes.ts +0 -5287
- package/src/fixer/index.ts +0 -6
- package/src/fixer/schemas.ts +0 -971
- package/src/fixer.ts +0 -642
- package/src/frameworks/detector.test.ts +0 -248
- package/src/frameworks/detector.ts +0 -899
- package/src/frameworks/index.ts +0 -68
- package/src/frameworks/recipes/angular.yaml +0 -171
- package/src/frameworks/recipes/astro.yaml +0 -206
- package/src/frameworks/recipes/django.yaml +0 -180
- package/src/frameworks/recipes/laravel.yaml +0 -137
- package/src/frameworks/recipes/nextjs.yaml +0 -268
- package/src/frameworks/recipes/nuxt.yaml +0 -175
- package/src/frameworks/recipes/rails.yaml +0 -188
- package/src/frameworks/recipes/react.yaml +0 -202
- package/src/frameworks/recipes/sveltekit.yaml +0 -154
- package/src/frameworks/recipes/vue.yaml +0 -137
- package/src/frameworks/recipes/wordpress.yaml +0 -209
- package/src/frameworks/suggestion-engine.ts +0 -357
- package/src/geo/geo-content.test.ts +0 -305
- package/src/geo/geo-content.ts +0 -266
- package/src/geo/geo-history.test.ts +0 -473
- package/src/geo/geo-history.ts +0 -433
- package/src/geo/geo-tracker.test.ts +0 -359
- package/src/geo/geo-tracker.ts +0 -411
- package/src/geo/index.ts +0 -11
- package/src/geo/llm-citation-checker.ts +0 -188
- package/src/git/commit-helper.test.ts +0 -261
- package/src/git/commit-helper.ts +0 -329
- package/src/git/index.ts +0 -12
- package/src/git/pr-helper.test.ts +0 -284
- package/src/git/pr-helper.ts +0 -307
- package/src/index.ts +0 -76
- package/src/keywords/ai-keyword-engine.ts +0 -1062
- package/src/keywords/ai-summarizer.ts +0 -387
- package/src/keywords/ci-mode.ts +0 -555
- package/src/keywords/engine.ts +0 -359
- package/src/keywords/index.ts +0 -151
- package/src/keywords/llm-judge.ts +0 -357
- package/src/keywords/nlp-analysis.ts +0 -706
- package/src/keywords/prioritizer.ts +0 -295
- package/src/keywords/site-crawler.ts +0 -342
- package/src/keywords/sources/autocomplete.ts +0 -139
- package/src/keywords/sources/competitive-search.ts +0 -450
- package/src/keywords/sources/competitor-analysis.ts +0 -374
- package/src/keywords/sources/dataforseo.ts +0 -206
- package/src/keywords/sources/free-sources.ts +0 -294
- package/src/keywords/sources/gsc.ts +0 -123
- package/src/keywords/topic-grouping.ts +0 -327
- package/src/keywords/types.ts +0 -144
- package/src/keywords/wizard.ts +0 -457
- package/src/loader.ts +0 -40
- package/src/ranking/index.ts +0 -5
- package/src/ranking/serp-client.ts +0 -348
- package/src/ranking/tracker.ts +0 -380
- package/src/ranking/types.ts +0 -123
- package/src/reports/index.ts +0 -7
- package/src/reports/report-generator.test.ts +0 -293
- package/src/reports/report-generator.ts +0 -713
- package/src/scheduler/alerts.test.ts +0 -458
- package/src/scheduler/alerts.ts +0 -328
- package/src/scheduler/index.ts +0 -8
- package/src/scheduler/scheduled-audit.test.ts +0 -377
- package/src/scheduler/scheduled-audit.ts +0 -149
- package/src/test/integration-test.ts +0 -325
- package/src/tools/analyzer.ts +0 -373
- package/src/tools/crawl.ts +0 -293
- package/src/tools/files.ts +0 -301
- package/src/tools/h1-fixer.ts +0 -249
- package/src/tools/index.ts +0 -67
- package/src/tracking/github-action.ts +0 -326
- package/src/tracking/google-analytics.ts +0 -265
- package/src/tracking/index.ts +0 -45
- package/src/tracking/report-generator.ts +0 -386
- package/src/tracking/search-console.ts +0 -335
- package/src/types.ts +0 -148
- package/src/utils/http.ts +0 -302
- package/src/wasm-adapter.ts +0 -297
- package/src/wasm-entry.ts +0 -14
- package/tsconfig.json +0 -17
- package/tsup.wasm.config.ts +0 -26
- package/vitest.config.ts +0 -15
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
# WordPress SEO Recipe
|
|
2
|
-
# WordPress powers 40%+ of the web - SEO is well-supported with plugins
|
|
3
|
-
|
|
4
|
-
framework: wordpress
|
|
5
|
-
display_name: WordPress
|
|
6
|
-
category: cms
|
|
7
|
-
ssr_default: true # PHP renders on server
|
|
8
|
-
|
|
9
|
-
seo_strengths:
|
|
10
|
-
- "Server-rendered HTML by default"
|
|
11
|
-
- "Mature SEO plugin ecosystem"
|
|
12
|
-
- "Built-in canonical URL support"
|
|
13
|
-
- "Sitemap generation via plugins"
|
|
14
|
-
- "Structured data plugins available"
|
|
15
|
-
|
|
16
|
-
seo_challenges:
|
|
17
|
-
- "Plugin bloat can slow performance"
|
|
18
|
-
- "Default themes often lack optimization"
|
|
19
|
-
- "Image optimization requires plugins"
|
|
20
|
-
- "Caching needed for performance"
|
|
21
|
-
|
|
22
|
-
checks:
|
|
23
|
-
- code: WORDPRESS_NO_SEO_PLUGIN
|
|
24
|
-
severity: warning
|
|
25
|
-
title: "No SEO plugin detected"
|
|
26
|
-
description: "WordPress benefits greatly from an SEO plugin like Yoast or Rank Math."
|
|
27
|
-
howToFix: |
|
|
28
|
-
Install an SEO plugin:
|
|
29
|
-
|
|
30
|
-
**Yoast SEO** (most popular):
|
|
31
|
-
1. Go to Plugins > Add New
|
|
32
|
-
2. Search "Yoast SEO"
|
|
33
|
-
3. Install and activate
|
|
34
|
-
|
|
35
|
-
**Rank Math** (feature-rich free tier):
|
|
36
|
-
1. Go to Plugins > Add New
|
|
37
|
-
2. Search "Rank Math"
|
|
38
|
-
3. Install and activate
|
|
39
|
-
|
|
40
|
-
Both provide:
|
|
41
|
-
- Meta title/description management
|
|
42
|
-
- XML sitemap generation
|
|
43
|
-
- Schema markup
|
|
44
|
-
- Content analysis
|
|
45
|
-
|
|
46
|
-
- code: WORDPRESS_OUTDATED
|
|
47
|
-
severity: error
|
|
48
|
-
title: "WordPress core is outdated"
|
|
49
|
-
description: "Running outdated WordPress is a security risk and may have SEO bugs."
|
|
50
|
-
howToFix: |
|
|
51
|
-
Update WordPress:
|
|
52
|
-
1. Backup your site first
|
|
53
|
-
2. Go to Dashboard > Updates
|
|
54
|
-
3. Click "Update Now"
|
|
55
|
-
|
|
56
|
-
Enable auto-updates in wp-config.php:
|
|
57
|
-
```php
|
|
58
|
-
define('WP_AUTO_UPDATE_CORE', true);
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
- code: WORDPRESS_NO_CACHING
|
|
62
|
-
severity: warning
|
|
63
|
-
title: "No caching plugin detected"
|
|
64
|
-
description: "WordPress without caching is slow, hurting Core Web Vitals."
|
|
65
|
-
howToFix: |
|
|
66
|
-
Install a caching plugin:
|
|
67
|
-
|
|
68
|
-
**WP Super Cache** (simple):
|
|
69
|
-
- Easy setup, good for most sites
|
|
70
|
-
|
|
71
|
-
**W3 Total Cache** (advanced):
|
|
72
|
-
- More features, steeper learning curve
|
|
73
|
-
|
|
74
|
-
**LiteSpeed Cache** (if on LiteSpeed server):
|
|
75
|
-
- Best performance on compatible hosts
|
|
76
|
-
|
|
77
|
-
- code: WORDPRESS_LARGE_IMAGES
|
|
78
|
-
severity: warning
|
|
79
|
-
title: "Images not optimized"
|
|
80
|
-
description: "Large images slow down WordPress significantly."
|
|
81
|
-
howToFix: |
|
|
82
|
-
Install an image optimization plugin:
|
|
83
|
-
|
|
84
|
-
**ShortPixel** or **Smush**:
|
|
85
|
-
- Compresses existing images
|
|
86
|
-
- Optimizes on upload
|
|
87
|
-
- WebP conversion
|
|
88
|
-
|
|
89
|
-
Or use Cloudflare Polish for automatic optimization.
|
|
90
|
-
|
|
91
|
-
- code: WORDPRESS_TOO_MANY_PLUGINS
|
|
92
|
-
severity: warning
|
|
93
|
-
title: "Too many plugins installed"
|
|
94
|
-
description: "Each plugin adds overhead. Many sites have 30+ plugins when 10-15 suffice."
|
|
95
|
-
howToFix: |
|
|
96
|
-
Audit your plugins:
|
|
97
|
-
1. Go to Plugins > Installed Plugins
|
|
98
|
-
2. Deactivate plugins not in use
|
|
99
|
-
3. Delete deactivated plugins
|
|
100
|
-
4. Consider consolidating (one SEO plugin, one security plugin)
|
|
101
|
-
|
|
102
|
-
Aim for under 20 plugins.
|
|
103
|
-
|
|
104
|
-
- code: WORDPRESS_NO_LAZY_LOAD
|
|
105
|
-
severity: notice
|
|
106
|
-
title: "Native lazy loading not enabled"
|
|
107
|
-
description: "WordPress 5.5+ has native lazy loading, but themes may disable it."
|
|
108
|
-
howToFix: |
|
|
109
|
-
Check if lazy loading is enabled:
|
|
110
|
-
```php
|
|
111
|
-
// In functions.php, remove if present:
|
|
112
|
-
add_filter('wp_lazy_loading_enabled', '__return_false');
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
For older WordPress, add:
|
|
116
|
-
```php
|
|
117
|
-
add_filter('wp_lazy_loading_enabled', '__return_true');
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
- code: WORDPRESS_NO_WEBP
|
|
121
|
-
severity: notice
|
|
122
|
-
title: "Not serving WebP images"
|
|
123
|
-
description: "WebP images are 25-35% smaller than JPEG."
|
|
124
|
-
howToFix: |
|
|
125
|
-
Use a plugin that serves WebP:
|
|
126
|
-
|
|
127
|
-
1. **ShortPixel**: Auto-converts and serves WebP
|
|
128
|
-
2. **WebP Express**: Converts and configures server
|
|
129
|
-
|
|
130
|
-
Or use Cloudflare with Polish enabled for automatic WebP.
|
|
131
|
-
|
|
132
|
-
- code: WORDPRESS_NO_PRELOAD_HINTS
|
|
133
|
-
severity: notice
|
|
134
|
-
title: "Missing resource hints"
|
|
135
|
-
description: "Preload hints can speed up critical resources."
|
|
136
|
-
howToFix: |
|
|
137
|
-
Add to your theme's functions.php:
|
|
138
|
-
|
|
139
|
-
```php
|
|
140
|
-
function add_resource_hints() {
|
|
141
|
-
// Preload critical font
|
|
142
|
-
echo '<link rel="preload" href="/wp-content/themes/yourtheme/fonts/main.woff2" as="font" type="font/woff2" crossorigin>';
|
|
143
|
-
|
|
144
|
-
// Preconnect to external domains
|
|
145
|
-
echo '<link rel="preconnect" href="https://fonts.googleapis.com">';
|
|
146
|
-
}
|
|
147
|
-
add_action('wp_head', 'add_resource_hints', 1);
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
- code: WORDPRESS_BLOCKING_SCRIPTS
|
|
151
|
-
severity: warning
|
|
152
|
-
title: "Render-blocking scripts detected"
|
|
153
|
-
description: "JavaScript in <head> blocks rendering."
|
|
154
|
-
howToFix: |
|
|
155
|
-
Use a plugin like **Autoptimize** or **Asset CleanUp** to:
|
|
156
|
-
- Defer JavaScript
|
|
157
|
-
- Inline critical CSS
|
|
158
|
-
- Combine files
|
|
159
|
-
|
|
160
|
-
Or add defer manually in functions.php:
|
|
161
|
-
```php
|
|
162
|
-
function defer_scripts($tag, $handle) {
|
|
163
|
-
if (is_admin()) return $tag;
|
|
164
|
-
return str_replace(' src', ' defer src', $tag);
|
|
165
|
-
}
|
|
166
|
-
add_filter('script_loader_tag', 'defer_scripts', 10, 2);
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
- code: WORDPRESS_NO_SCHEMA
|
|
170
|
-
severity: warning
|
|
171
|
-
title: "No structured data detected"
|
|
172
|
-
description: "Schema markup helps with rich snippets."
|
|
173
|
-
howToFix: |
|
|
174
|
-
**If using Yoast/Rank Math**: Schema is included.
|
|
175
|
-
|
|
176
|
-
**Manual schema** in theme:
|
|
177
|
-
```php
|
|
178
|
-
function add_organization_schema() {
|
|
179
|
-
if (is_front_page()) {
|
|
180
|
-
echo '<script type="application/ld+json">
|
|
181
|
-
{
|
|
182
|
-
"@context": "https://schema.org",
|
|
183
|
-
"@type": "Organization",
|
|
184
|
-
"name": "Your Company",
|
|
185
|
-
"url": "' . home_url() . '"
|
|
186
|
-
}
|
|
187
|
-
</script>';
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
add_action('wp_footer', 'add_organization_schema');
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
performance_tips:
|
|
194
|
-
- "Use a quality managed WordPress host (WP Engine, Kinsta)"
|
|
195
|
-
- "Enable object caching (Redis/Memcached)"
|
|
196
|
-
- "Use a CDN (Cloudflare, Fastly)"
|
|
197
|
-
- "Keep plugins under 20"
|
|
198
|
-
- "Choose a lightweight theme (GeneratePress, Astra)"
|
|
199
|
-
- "Disable unnecessary features (emojis, embeds)"
|
|
200
|
-
|
|
201
|
-
recommended_plugins:
|
|
202
|
-
seo:
|
|
203
|
-
- "Yoast SEO or Rank Math"
|
|
204
|
-
performance:
|
|
205
|
-
- "WP Super Cache or LiteSpeed Cache"
|
|
206
|
-
- "ShortPixel or Smush (images)"
|
|
207
|
-
- "Autoptimize (JS/CSS)"
|
|
208
|
-
security:
|
|
209
|
-
- "Wordfence or Sucuri"
|
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Framework-Specific SEO Suggestion Engine
|
|
3
|
-
*
|
|
4
|
-
* Loads recipe files and provides framework-specific SEO suggestions.
|
|
5
|
-
* Works with both CLI and Web UI.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { parse as parseYaml } from 'yaml';
|
|
9
|
-
import type { Framework, FrameworkInfo } from './detector.js';
|
|
10
|
-
import type { AuditIssue, IssueCategory, IssueSeverity } from '../audit/types.js';
|
|
11
|
-
|
|
12
|
-
export interface FrameworkCheck {
|
|
13
|
-
code: string;
|
|
14
|
-
severity: IssueSeverity;
|
|
15
|
-
title: string;
|
|
16
|
-
description: string;
|
|
17
|
-
howToFix: string;
|
|
18
|
-
impact?: string;
|
|
19
|
-
detection?: {
|
|
20
|
-
html_patterns?: string[];
|
|
21
|
-
missing_content?: boolean;
|
|
22
|
-
title_in_js?: boolean;
|
|
23
|
-
title_not_in_html?: boolean;
|
|
24
|
-
};
|
|
25
|
-
app_router_only?: boolean;
|
|
26
|
-
pages_router_only?: boolean;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface FrameworkRecipe {
|
|
30
|
-
framework: Framework;
|
|
31
|
-
display_name: string;
|
|
32
|
-
category: string;
|
|
33
|
-
base_framework?: Framework;
|
|
34
|
-
ssr_default: boolean;
|
|
35
|
-
seo_challenges?: string[];
|
|
36
|
-
seo_strengths?: string[];
|
|
37
|
-
checks: FrameworkCheck[];
|
|
38
|
-
performance_tips?: string[];
|
|
39
|
-
migration_paths?: Record<string, {
|
|
40
|
-
effort: string;
|
|
41
|
-
benefits: string[];
|
|
42
|
-
guide?: string;
|
|
43
|
-
}>;
|
|
44
|
-
recommended_plugins?: Record<string, string[]>;
|
|
45
|
-
recommended_gems?: Record<string, string[]>;
|
|
46
|
-
integrations?: string[];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Embedded recipes (loaded at build time or dynamically)
|
|
50
|
-
const RECIPES: Record<Framework, FrameworkRecipe | null> = {
|
|
51
|
-
// JavaScript/TypeScript
|
|
52
|
-
react: null,
|
|
53
|
-
nextjs: null,
|
|
54
|
-
vue: null,
|
|
55
|
-
nuxt: null,
|
|
56
|
-
angular: null,
|
|
57
|
-
svelte: null,
|
|
58
|
-
sveltekit: null,
|
|
59
|
-
astro: null,
|
|
60
|
-
remix: null,
|
|
61
|
-
gatsby: null,
|
|
62
|
-
solidjs: null,
|
|
63
|
-
qwik: null,
|
|
64
|
-
fresh: null,
|
|
65
|
-
|
|
66
|
-
// Ruby
|
|
67
|
-
rails: null,
|
|
68
|
-
|
|
69
|
-
// Python
|
|
70
|
-
django: null,
|
|
71
|
-
flask: null,
|
|
72
|
-
fastapi: null,
|
|
73
|
-
|
|
74
|
-
// PHP
|
|
75
|
-
laravel: null,
|
|
76
|
-
symfony: null,
|
|
77
|
-
wordpress: null,
|
|
78
|
-
|
|
79
|
-
// Java
|
|
80
|
-
spring: null,
|
|
81
|
-
|
|
82
|
-
// C#
|
|
83
|
-
aspnet: null,
|
|
84
|
-
|
|
85
|
-
// Go
|
|
86
|
-
go: null,
|
|
87
|
-
|
|
88
|
-
// Elixir
|
|
89
|
-
phoenix: null,
|
|
90
|
-
|
|
91
|
-
// Hypermedia
|
|
92
|
-
htmx: null,
|
|
93
|
-
hotwire: null,
|
|
94
|
-
|
|
95
|
-
// Static Site Generators
|
|
96
|
-
hugo: null,
|
|
97
|
-
jekyll: null,
|
|
98
|
-
eleventy: null,
|
|
99
|
-
pelican: null,
|
|
100
|
-
|
|
101
|
-
// Unknown
|
|
102
|
-
unknown: null,
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// Recipe content will be bundled inline for distribution
|
|
106
|
-
// This allows the suggestion engine to work without file system access
|
|
107
|
-
const RECIPE_CONTENT: Record<string, string> = {};
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Load a recipe from embedded content or file system
|
|
111
|
-
*/
|
|
112
|
-
export async function loadRecipe(framework: Framework): Promise<FrameworkRecipe | null> {
|
|
113
|
-
if (framework === 'unknown') return null;
|
|
114
|
-
|
|
115
|
-
// Check cache
|
|
116
|
-
if (RECIPES[framework]) {
|
|
117
|
-
return RECIPES[framework];
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Check embedded content
|
|
121
|
-
if (RECIPE_CONTENT[framework]) {
|
|
122
|
-
try {
|
|
123
|
-
const recipe = parseYaml(RECIPE_CONTENT[framework]) as FrameworkRecipe;
|
|
124
|
-
RECIPES[framework] = recipe;
|
|
125
|
-
return recipe;
|
|
126
|
-
} catch {
|
|
127
|
-
console.warn(`Failed to parse embedded recipe for ${framework}`);
|
|
128
|
-
return null;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Try dynamic import for Node.js environment
|
|
133
|
-
try {
|
|
134
|
-
const fs = await import('fs');
|
|
135
|
-
const path = await import('path');
|
|
136
|
-
const url = await import('url');
|
|
137
|
-
|
|
138
|
-
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
139
|
-
const recipePath = path.join(__dirname, 'recipes', `${framework}.yaml`);
|
|
140
|
-
|
|
141
|
-
if (fs.existsSync(recipePath)) {
|
|
142
|
-
const content = fs.readFileSync(recipePath, 'utf-8');
|
|
143
|
-
const recipe = parseYaml(content) as FrameworkRecipe;
|
|
144
|
-
RECIPES[framework] = recipe;
|
|
145
|
-
return recipe;
|
|
146
|
-
}
|
|
147
|
-
} catch {
|
|
148
|
-
// File system not available (browser, edge function)
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Get framework-specific issues based on HTML analysis
|
|
156
|
-
*/
|
|
157
|
-
export async function getFrameworkIssues(
|
|
158
|
-
frameworkInfo: FrameworkInfo,
|
|
159
|
-
html: string,
|
|
160
|
-
url: string
|
|
161
|
-
): Promise<AuditIssue[]> {
|
|
162
|
-
const recipe = await loadRecipe(frameworkInfo.framework);
|
|
163
|
-
if (!recipe) return [];
|
|
164
|
-
|
|
165
|
-
const issues: AuditIssue[] = [];
|
|
166
|
-
|
|
167
|
-
for (const check of recipe.checks) {
|
|
168
|
-
const matched = evaluateCheck(check, html, frameworkInfo);
|
|
169
|
-
if (matched) {
|
|
170
|
-
issues.push({
|
|
171
|
-
code: check.code,
|
|
172
|
-
severity: check.severity,
|
|
173
|
-
category: 'on-page' as IssueCategory, // Framework issues are on-page
|
|
174
|
-
title: check.title,
|
|
175
|
-
description: check.description,
|
|
176
|
-
impact: check.impact || `Framework-specific issue for ${recipe.display_name}`,
|
|
177
|
-
howToFix: check.howToFix,
|
|
178
|
-
affectedUrls: [url],
|
|
179
|
-
details: {
|
|
180
|
-
framework: recipe.display_name,
|
|
181
|
-
frameworkCategory: recipe.category,
|
|
182
|
-
},
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
return issues;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Evaluate if a check matches the HTML content
|
|
192
|
-
*/
|
|
193
|
-
function evaluateCheck(
|
|
194
|
-
check: FrameworkCheck,
|
|
195
|
-
html: string,
|
|
196
|
-
frameworkInfo: FrameworkInfo
|
|
197
|
-
): boolean {
|
|
198
|
-
// Skip router-specific checks
|
|
199
|
-
if (check.app_router_only && !html.includes('__next')) {
|
|
200
|
-
return false;
|
|
201
|
-
}
|
|
202
|
-
if (check.pages_router_only && html.includes('__next')) {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const detection = check.detection;
|
|
207
|
-
if (!detection) {
|
|
208
|
-
// No detection rules, check is informational only
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// Check HTML patterns
|
|
213
|
-
if (detection.html_patterns) {
|
|
214
|
-
for (const pattern of detection.html_patterns) {
|
|
215
|
-
if (html.includes(pattern)) {
|
|
216
|
-
return true;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Check for missing content (SPA detection)
|
|
222
|
-
if (detection.missing_content) {
|
|
223
|
-
const bodyMatch = html.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
|
224
|
-
if (bodyMatch) {
|
|
225
|
-
const bodyContent = bodyMatch[1]
|
|
226
|
-
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
|
227
|
-
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
|
228
|
-
.replace(/<[^>]+>/g, '')
|
|
229
|
-
.replace(/\s+/g, ' ')
|
|
230
|
-
.trim();
|
|
231
|
-
|
|
232
|
-
// Less than 100 chars of text content = likely empty SPA
|
|
233
|
-
if (bodyContent.length < 100) {
|
|
234
|
-
return true;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Check title in JS but not in HTML
|
|
240
|
-
if (detection.title_in_js && detection.title_not_in_html) {
|
|
241
|
-
const hasHtmlTitle = /<title[^>]*>[^<]+<\/title>/i.test(html);
|
|
242
|
-
const hasJsTitle = /document\.title\s*=/.test(html) ||
|
|
243
|
-
/useHead|Helmet|useSeoMeta/.test(html);
|
|
244
|
-
if (!hasHtmlTitle && hasJsTitle) {
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Get framework-specific suggestions for fixing issues
|
|
254
|
-
*/
|
|
255
|
-
export async function getFrameworkSuggestions(
|
|
256
|
-
frameworkInfo: FrameworkInfo,
|
|
257
|
-
issueCode: string
|
|
258
|
-
): Promise<string | null> {
|
|
259
|
-
const recipe = await loadRecipe(frameworkInfo.framework);
|
|
260
|
-
if (!recipe) return null;
|
|
261
|
-
|
|
262
|
-
const check = recipe.checks.find(c => c.code === issueCode);
|
|
263
|
-
if (check) {
|
|
264
|
-
return check.howToFix;
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
return null;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get all available checks for a framework
|
|
272
|
-
*/
|
|
273
|
-
export async function getFrameworkChecks(
|
|
274
|
-
framework: Framework
|
|
275
|
-
): Promise<FrameworkCheck[]> {
|
|
276
|
-
const recipe = await loadRecipe(framework);
|
|
277
|
-
if (!recipe) return [];
|
|
278
|
-
return recipe.checks;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Get performance tips for a framework
|
|
283
|
-
*/
|
|
284
|
-
export async function getPerformanceTips(
|
|
285
|
-
framework: Framework
|
|
286
|
-
): Promise<string[]> {
|
|
287
|
-
const recipe = await loadRecipe(framework);
|
|
288
|
-
if (!recipe) return [];
|
|
289
|
-
return recipe.performance_tips || [];
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Get SEO challenges for a framework
|
|
294
|
-
*/
|
|
295
|
-
export async function getSeoChallenges(
|
|
296
|
-
framework: Framework
|
|
297
|
-
): Promise<string[]> {
|
|
298
|
-
const recipe = await loadRecipe(framework);
|
|
299
|
-
if (!recipe) return [];
|
|
300
|
-
return recipe.seo_challenges || [];
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Get migration paths from current framework
|
|
305
|
-
*/
|
|
306
|
-
export async function getMigrationPaths(
|
|
307
|
-
framework: Framework
|
|
308
|
-
): Promise<Record<string, { effort: string; benefits: string[]; guide?: string }>> {
|
|
309
|
-
const recipe = await loadRecipe(framework);
|
|
310
|
-
if (!recipe) return {};
|
|
311
|
-
return recipe.migration_paths || {};
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Get total number of framework-specific checks
|
|
316
|
-
*/
|
|
317
|
-
export async function getTotalFrameworkChecks(): Promise<number> {
|
|
318
|
-
let total = 0;
|
|
319
|
-
for (const framework of Object.keys(RECIPES) as Framework[]) {
|
|
320
|
-
const recipe = await loadRecipe(framework);
|
|
321
|
-
if (recipe) {
|
|
322
|
-
total += recipe.checks.length;
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return total;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Get framework summary for display
|
|
330
|
-
*/
|
|
331
|
-
export async function getFrameworkSummary(framework: Framework): Promise<{
|
|
332
|
-
name: string;
|
|
333
|
-
category: string;
|
|
334
|
-
ssrDefault: boolean;
|
|
335
|
-
checkCount: number;
|
|
336
|
-
strengths: string[];
|
|
337
|
-
challenges: string[];
|
|
338
|
-
} | null> {
|
|
339
|
-
const recipe = await loadRecipe(framework);
|
|
340
|
-
if (!recipe) return null;
|
|
341
|
-
|
|
342
|
-
return {
|
|
343
|
-
name: recipe.display_name,
|
|
344
|
-
category: recipe.category,
|
|
345
|
-
ssrDefault: recipe.ssr_default,
|
|
346
|
-
checkCount: recipe.checks.length,
|
|
347
|
-
strengths: recipe.seo_strengths || [],
|
|
348
|
-
challenges: recipe.seo_challenges || [],
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Embed recipe content for bundling (called at build time)
|
|
354
|
-
*/
|
|
355
|
-
export function embedRecipe(framework: Framework, content: string): void {
|
|
356
|
-
RECIPE_CONTENT[framework] = content;
|
|
357
|
-
}
|