@rankcli/agent-runtime 0.0.1
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 +242 -0
- package/dist/analyzer-2CSWIQGD.mjs +6 -0
- package/dist/chunk-YNZYHEYM.mjs +774 -0
- package/dist/index.d.mts +4012 -0
- package/dist/index.d.ts +4012 -0
- package/dist/index.js +29672 -0
- package/dist/index.mjs +28602 -0
- package/package.json +53 -0
- package/scripts/build-deno.ts +134 -0
- package/src/audit/ai/analyzer.ts +347 -0
- package/src/audit/ai/index.ts +29 -0
- package/src/audit/ai/prompts/content-analysis.ts +271 -0
- package/src/audit/ai/types.ts +179 -0
- package/src/audit/checks/additional-checks.ts +439 -0
- package/src/audit/checks/ai-citation-worthiness.ts +399 -0
- package/src/audit/checks/ai-content-structure.ts +325 -0
- package/src/audit/checks/ai-readiness.ts +339 -0
- package/src/audit/checks/anchor-text.ts +179 -0
- package/src/audit/checks/answer-conciseness.ts +322 -0
- package/src/audit/checks/asset-minification.ts +270 -0
- package/src/audit/checks/bing-optimization.ts +206 -0
- package/src/audit/checks/brand-mention-optimization.ts +349 -0
- package/src/audit/checks/caching-headers.ts +305 -0
- package/src/audit/checks/canonical-advanced.ts +150 -0
- package/src/audit/checks/canonical-domain.ts +196 -0
- package/src/audit/checks/citation-quality.ts +358 -0
- package/src/audit/checks/client-rendering.ts +542 -0
- package/src/audit/checks/color-contrast.ts +342 -0
- package/src/audit/checks/content-freshness.ts +170 -0
- package/src/audit/checks/content-science.ts +589 -0
- package/src/audit/checks/conversion-elements.ts +526 -0
- package/src/audit/checks/crawlability.ts +220 -0
- package/src/audit/checks/directory-listing.ts +172 -0
- package/src/audit/checks/dom-analysis.ts +191 -0
- package/src/audit/checks/dom-size.ts +246 -0
- package/src/audit/checks/duplicate-content.ts +194 -0
- package/src/audit/checks/eeat-signals.ts +990 -0
- package/src/audit/checks/entity-seo.ts +396 -0
- package/src/audit/checks/featured-snippet.ts +473 -0
- package/src/audit/checks/freshness-signals.ts +443 -0
- package/src/audit/checks/funnel-intent.ts +463 -0
- package/src/audit/checks/hreflang.ts +174 -0
- package/src/audit/checks/html-compliance.ts +302 -0
- package/src/audit/checks/image-dimensions.ts +167 -0
- package/src/audit/checks/images.ts +160 -0
- package/src/audit/checks/indexnow.ts +275 -0
- package/src/audit/checks/interactive-tools.ts +475 -0
- package/src/audit/checks/internal-link-graph.ts +436 -0
- package/src/audit/checks/keyword-analysis.ts +239 -0
- package/src/audit/checks/keyword-cannibalization.ts +385 -0
- package/src/audit/checks/keyword-placement.ts +471 -0
- package/src/audit/checks/links.ts +203 -0
- package/src/audit/checks/llms-txt.ts +224 -0
- package/src/audit/checks/local-seo.ts +296 -0
- package/src/audit/checks/mobile.ts +167 -0
- package/src/audit/checks/modern-images.ts +226 -0
- package/src/audit/checks/navboost-signals.ts +395 -0
- package/src/audit/checks/on-page.ts +209 -0
- package/src/audit/checks/page-resources.ts +285 -0
- package/src/audit/checks/pagination.ts +180 -0
- package/src/audit/checks/performance.ts +153 -0
- package/src/audit/checks/platform-presence.ts +580 -0
- package/src/audit/checks/redirect-analysis.ts +153 -0
- package/src/audit/checks/redirect-chain.ts +389 -0
- package/src/audit/checks/resource-hints.ts +420 -0
- package/src/audit/checks/responsive-css.ts +247 -0
- package/src/audit/checks/responsive-images.ts +396 -0
- package/src/audit/checks/review-ecosystem.ts +415 -0
- package/src/audit/checks/robots-validation.ts +373 -0
- package/src/audit/checks/security-headers.ts +172 -0
- package/src/audit/checks/security.ts +144 -0
- package/src/audit/checks/serp-preview.ts +251 -0
- package/src/audit/checks/site-maturity.ts +444 -0
- package/src/audit/checks/social-meta.test.ts +275 -0
- package/src/audit/checks/social-meta.ts +134 -0
- package/src/audit/checks/soft-404.ts +151 -0
- package/src/audit/checks/structured-data.ts +238 -0
- package/src/audit/checks/tech-detection.ts +496 -0
- package/src/audit/checks/topical-clusters.ts +435 -0
- package/src/audit/checks/tracker-bloat.ts +462 -0
- package/src/audit/checks/tracking-verification.test.ts +371 -0
- package/src/audit/checks/tracking-verification.ts +636 -0
- package/src/audit/checks/url-safety.ts +682 -0
- package/src/audit/deno-entry.ts +66 -0
- package/src/audit/discovery/index.ts +15 -0
- package/src/audit/discovery/link-crawler.ts +232 -0
- package/src/audit/discovery/repo-routes.ts +347 -0
- package/src/audit/engine.ts +620 -0
- package/src/audit/fixes/index.ts +209 -0
- package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
- package/src/audit/fixes/social-meta-fixes.ts +463 -0
- package/src/audit/index.ts +74 -0
- package/src/audit/runner.test.ts +299 -0
- package/src/audit/runner.ts +130 -0
- package/src/audit/types.ts +1953 -0
- package/src/content/featured-snippet.ts +367 -0
- package/src/content/generator.test.ts +534 -0
- package/src/content/generator.ts +501 -0
- package/src/content/headline.ts +317 -0
- package/src/content/index.ts +62 -0
- package/src/content/intent.ts +258 -0
- package/src/content/keyword-density.ts +349 -0
- package/src/content/readability.ts +262 -0
- package/src/executor.ts +336 -0
- package/src/fixer.ts +416 -0
- package/src/frameworks/detector.test.ts +248 -0
- package/src/frameworks/detector.ts +371 -0
- package/src/frameworks/index.ts +68 -0
- package/src/frameworks/recipes/angular.yaml +171 -0
- package/src/frameworks/recipes/astro.yaml +206 -0
- package/src/frameworks/recipes/django.yaml +180 -0
- package/src/frameworks/recipes/laravel.yaml +137 -0
- package/src/frameworks/recipes/nextjs.yaml +268 -0
- package/src/frameworks/recipes/nuxt.yaml +175 -0
- package/src/frameworks/recipes/rails.yaml +188 -0
- package/src/frameworks/recipes/react.yaml +202 -0
- package/src/frameworks/recipes/sveltekit.yaml +154 -0
- package/src/frameworks/recipes/vue.yaml +137 -0
- package/src/frameworks/recipes/wordpress.yaml +209 -0
- package/src/frameworks/suggestion-engine.ts +320 -0
- package/src/geo/geo-content.test.ts +305 -0
- package/src/geo/geo-content.ts +266 -0
- package/src/geo/geo-history.test.ts +473 -0
- package/src/geo/geo-history.ts +433 -0
- package/src/geo/geo-tracker.test.ts +359 -0
- package/src/geo/geo-tracker.ts +411 -0
- package/src/geo/index.ts +10 -0
- package/src/git/commit-helper.test.ts +261 -0
- package/src/git/commit-helper.ts +329 -0
- package/src/git/index.ts +12 -0
- package/src/git/pr-helper.test.ts +284 -0
- package/src/git/pr-helper.ts +307 -0
- package/src/index.ts +66 -0
- package/src/keywords/ai-keyword-engine.ts +1062 -0
- package/src/keywords/ai-summarizer.ts +387 -0
- package/src/keywords/ci-mode.ts +555 -0
- package/src/keywords/engine.ts +359 -0
- package/src/keywords/index.ts +151 -0
- package/src/keywords/llm-judge.ts +357 -0
- package/src/keywords/nlp-analysis.ts +706 -0
- package/src/keywords/prioritizer.ts +295 -0
- package/src/keywords/site-crawler.ts +342 -0
- package/src/keywords/sources/autocomplete.ts +139 -0
- package/src/keywords/sources/competitive-search.ts +450 -0
- package/src/keywords/sources/competitor-analysis.ts +374 -0
- package/src/keywords/sources/dataforseo.ts +206 -0
- package/src/keywords/sources/free-sources.ts +294 -0
- package/src/keywords/sources/gsc.ts +123 -0
- package/src/keywords/topic-grouping.ts +327 -0
- package/src/keywords/types.ts +144 -0
- package/src/keywords/wizard.ts +457 -0
- package/src/loader.ts +40 -0
- package/src/reports/index.ts +7 -0
- package/src/reports/report-generator.test.ts +293 -0
- package/src/reports/report-generator.ts +713 -0
- package/src/scheduler/alerts.test.ts +458 -0
- package/src/scheduler/alerts.ts +328 -0
- package/src/scheduler/index.ts +8 -0
- package/src/scheduler/scheduled-audit.test.ts +377 -0
- package/src/scheduler/scheduled-audit.ts +149 -0
- package/src/test/integration-test.ts +325 -0
- package/src/tools/analyzer.ts +373 -0
- package/src/tools/crawl.ts +293 -0
- package/src/tools/files.ts +301 -0
- package/src/tools/h1-fixer.ts +249 -0
- package/src/tools/index.ts +67 -0
- package/src/tracking/github-action.ts +326 -0
- package/src/tracking/google-analytics.ts +265 -0
- package/src/tracking/index.ts +45 -0
- package/src/tracking/report-generator.ts +386 -0
- package/src/tracking/search-console.ts +335 -0
- package/src/types.ts +134 -0
- package/src/utils/http.ts +302 -0
- package/src/wasm-adapter.ts +297 -0
- package/src/wasm-entry.ts +14 -0
- package/tsconfig.json +17 -0
- package/tsup.wasm.config.ts +26 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Detection
|
|
3
|
+
*
|
|
4
|
+
* Detects web frameworks from:
|
|
5
|
+
* 1. HTML signatures (data attributes, meta tags, scripts)
|
|
6
|
+
* 2. package.json dependencies
|
|
7
|
+
* 3. File structure patterns
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type Framework =
|
|
11
|
+
| 'react'
|
|
12
|
+
| 'nextjs'
|
|
13
|
+
| 'vue'
|
|
14
|
+
| 'nuxt'
|
|
15
|
+
| 'angular'
|
|
16
|
+
| 'svelte'
|
|
17
|
+
| 'sveltekit'
|
|
18
|
+
| 'astro'
|
|
19
|
+
| 'remix'
|
|
20
|
+
| 'gatsby'
|
|
21
|
+
| 'rails'
|
|
22
|
+
| 'laravel'
|
|
23
|
+
| 'django'
|
|
24
|
+
| 'wordpress'
|
|
25
|
+
| 'unknown';
|
|
26
|
+
|
|
27
|
+
export interface FrameworkInfo {
|
|
28
|
+
framework: Framework;
|
|
29
|
+
version?: string;
|
|
30
|
+
confidence: 'high' | 'medium' | 'low';
|
|
31
|
+
detected_from: 'html' | 'package.json' | 'files' | 'headers';
|
|
32
|
+
meta_framework?: Framework; // e.g., Next.js for React
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface HtmlSignature {
|
|
36
|
+
framework: Framework;
|
|
37
|
+
patterns: RegExp[];
|
|
38
|
+
confidence: 'high' | 'medium' | 'low';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const HTML_SIGNATURES: HtmlSignature[] = [
|
|
42
|
+
// Next.js
|
|
43
|
+
{
|
|
44
|
+
framework: 'nextjs',
|
|
45
|
+
patterns: [
|
|
46
|
+
/__next/,
|
|
47
|
+
/_next\//,
|
|
48
|
+
/data-nscript/,
|
|
49
|
+
/__NEXT_DATA__/,
|
|
50
|
+
/next\/script/,
|
|
51
|
+
],
|
|
52
|
+
confidence: 'high',
|
|
53
|
+
},
|
|
54
|
+
// Nuxt
|
|
55
|
+
{
|
|
56
|
+
framework: 'nuxt',
|
|
57
|
+
patterns: [
|
|
58
|
+
/__nuxt/,
|
|
59
|
+
/_nuxt\//,
|
|
60
|
+
/data-n-head/,
|
|
61
|
+
/nuxt-link/,
|
|
62
|
+
/__NUXT__/,
|
|
63
|
+
],
|
|
64
|
+
confidence: 'high',
|
|
65
|
+
},
|
|
66
|
+
// Gatsby
|
|
67
|
+
{
|
|
68
|
+
framework: 'gatsby',
|
|
69
|
+
patterns: [
|
|
70
|
+
/___gatsby/,
|
|
71
|
+
/gatsby-/,
|
|
72
|
+
/gatsby-image/,
|
|
73
|
+
/data-gatsby/,
|
|
74
|
+
],
|
|
75
|
+
confidence: 'high',
|
|
76
|
+
},
|
|
77
|
+
// Astro
|
|
78
|
+
{
|
|
79
|
+
framework: 'astro',
|
|
80
|
+
patterns: [
|
|
81
|
+
/astro-/,
|
|
82
|
+
/data-astro/,
|
|
83
|
+
/_astro\//,
|
|
84
|
+
],
|
|
85
|
+
confidence: 'high',
|
|
86
|
+
},
|
|
87
|
+
// SvelteKit
|
|
88
|
+
{
|
|
89
|
+
framework: 'sveltekit',
|
|
90
|
+
patterns: [
|
|
91
|
+
/__sveltekit/,
|
|
92
|
+
/_app\/immutable/,
|
|
93
|
+
/svelte-kit/,
|
|
94
|
+
],
|
|
95
|
+
confidence: 'high',
|
|
96
|
+
},
|
|
97
|
+
// Svelte (standalone)
|
|
98
|
+
{
|
|
99
|
+
framework: 'svelte',
|
|
100
|
+
patterns: [
|
|
101
|
+
/svelte-\w+/,
|
|
102
|
+
/__svelte/,
|
|
103
|
+
],
|
|
104
|
+
confidence: 'medium',
|
|
105
|
+
},
|
|
106
|
+
// Remix
|
|
107
|
+
{
|
|
108
|
+
framework: 'remix',
|
|
109
|
+
patterns: [
|
|
110
|
+
/__remix/,
|
|
111
|
+
/remix-/,
|
|
112
|
+
/data-remix/,
|
|
113
|
+
],
|
|
114
|
+
confidence: 'high',
|
|
115
|
+
},
|
|
116
|
+
// React (generic - lower priority than Next/Gatsby/Remix)
|
|
117
|
+
{
|
|
118
|
+
framework: 'react',
|
|
119
|
+
patterns: [
|
|
120
|
+
/data-reactroot/,
|
|
121
|
+
/data-react-helmet/,
|
|
122
|
+
/_react/,
|
|
123
|
+
/react-app/,
|
|
124
|
+
],
|
|
125
|
+
confidence: 'medium',
|
|
126
|
+
},
|
|
127
|
+
// Vue (generic - lower priority than Nuxt)
|
|
128
|
+
{
|
|
129
|
+
framework: 'vue',
|
|
130
|
+
patterns: [
|
|
131
|
+
/data-v-[a-f0-9]+/,
|
|
132
|
+
/v-cloak/,
|
|
133
|
+
/__vue/,
|
|
134
|
+
],
|
|
135
|
+
confidence: 'medium',
|
|
136
|
+
},
|
|
137
|
+
// Angular
|
|
138
|
+
{
|
|
139
|
+
framework: 'angular',
|
|
140
|
+
patterns: [
|
|
141
|
+
/ng-version/,
|
|
142
|
+
/_ngcontent/,
|
|
143
|
+
/ng-\w+=/,
|
|
144
|
+
/\[ng/,
|
|
145
|
+
],
|
|
146
|
+
confidence: 'high',
|
|
147
|
+
},
|
|
148
|
+
// WordPress
|
|
149
|
+
{
|
|
150
|
+
framework: 'wordpress',
|
|
151
|
+
patterns: [
|
|
152
|
+
/wp-content/,
|
|
153
|
+
/wp-includes/,
|
|
154
|
+
/wp-json/,
|
|
155
|
+
/wordpress/i,
|
|
156
|
+
],
|
|
157
|
+
confidence: 'high',
|
|
158
|
+
},
|
|
159
|
+
// Laravel
|
|
160
|
+
{
|
|
161
|
+
framework: 'laravel',
|
|
162
|
+
patterns: [
|
|
163
|
+
/laravel_session/,
|
|
164
|
+
/XSRF-TOKEN/,
|
|
165
|
+
/@csrf/,
|
|
166
|
+
],
|
|
167
|
+
confidence: 'medium',
|
|
168
|
+
},
|
|
169
|
+
// Rails
|
|
170
|
+
{
|
|
171
|
+
framework: 'rails',
|
|
172
|
+
patterns: [
|
|
173
|
+
/csrf-token/,
|
|
174
|
+
/turbolinks/,
|
|
175
|
+
/data-turbo/,
|
|
176
|
+
/rails-ujs/,
|
|
177
|
+
],
|
|
178
|
+
confidence: 'medium',
|
|
179
|
+
},
|
|
180
|
+
// Django
|
|
181
|
+
{
|
|
182
|
+
framework: 'django',
|
|
183
|
+
patterns: [
|
|
184
|
+
/csrfmiddlewaretoken/,
|
|
185
|
+
/django/,
|
|
186
|
+
],
|
|
187
|
+
confidence: 'medium',
|
|
188
|
+
},
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
interface PackageDependency {
|
|
192
|
+
framework: Framework;
|
|
193
|
+
packages: string[];
|
|
194
|
+
meta_framework?: Framework;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const PACKAGE_DEPENDENCIES: PackageDependency[] = [
|
|
198
|
+
{ framework: 'nextjs', packages: ['next'], meta_framework: 'react' },
|
|
199
|
+
{ framework: 'gatsby', packages: ['gatsby'], meta_framework: 'react' },
|
|
200
|
+
{ framework: 'remix', packages: ['@remix-run/react', '@remix-run/node'], meta_framework: 'react' },
|
|
201
|
+
{ framework: 'nuxt', packages: ['nuxt', 'nuxt3'], meta_framework: 'vue' },
|
|
202
|
+
{ framework: 'sveltekit', packages: ['@sveltejs/kit'], meta_framework: 'svelte' },
|
|
203
|
+
{ framework: 'astro', packages: ['astro'] },
|
|
204
|
+
{ framework: 'react', packages: ['react', 'react-dom'] },
|
|
205
|
+
{ framework: 'vue', packages: ['vue'] },
|
|
206
|
+
{ framework: 'angular', packages: ['@angular/core'] },
|
|
207
|
+
{ framework: 'svelte', packages: ['svelte'] },
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Detect framework from HTML content
|
|
212
|
+
*/
|
|
213
|
+
export function detectFromHtml(html: string): FrameworkInfo | null {
|
|
214
|
+
for (const signature of HTML_SIGNATURES) {
|
|
215
|
+
for (const pattern of signature.patterns) {
|
|
216
|
+
if (pattern.test(html)) {
|
|
217
|
+
return {
|
|
218
|
+
framework: signature.framework,
|
|
219
|
+
confidence: signature.confidence,
|
|
220
|
+
detected_from: 'html',
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Detect framework from package.json content
|
|
230
|
+
*/
|
|
231
|
+
export function detectFromPackageJson(packageJson: {
|
|
232
|
+
dependencies?: Record<string, string>;
|
|
233
|
+
devDependencies?: Record<string, string>;
|
|
234
|
+
}): FrameworkInfo | null {
|
|
235
|
+
const allDeps = {
|
|
236
|
+
...packageJson.dependencies,
|
|
237
|
+
...packageJson.devDependencies,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
for (const dep of PACKAGE_DEPENDENCIES) {
|
|
241
|
+
for (const pkg of dep.packages) {
|
|
242
|
+
if (allDeps[pkg]) {
|
|
243
|
+
return {
|
|
244
|
+
framework: dep.framework,
|
|
245
|
+
version: allDeps[pkg],
|
|
246
|
+
confidence: 'high',
|
|
247
|
+
detected_from: 'package.json',
|
|
248
|
+
meta_framework: dep.meta_framework,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
return null;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Detect framework from HTTP response headers
|
|
259
|
+
*/
|
|
260
|
+
export function detectFromHeaders(headers: Record<string, string>): FrameworkInfo | null {
|
|
261
|
+
const poweredBy = headers['x-powered-by']?.toLowerCase() || '';
|
|
262
|
+
const server = headers['server']?.toLowerCase() || '';
|
|
263
|
+
|
|
264
|
+
if (poweredBy.includes('next.js') || poweredBy.includes('next')) {
|
|
265
|
+
return { framework: 'nextjs', confidence: 'high', detected_from: 'headers' };
|
|
266
|
+
}
|
|
267
|
+
if (poweredBy.includes('nuxt')) {
|
|
268
|
+
return { framework: 'nuxt', confidence: 'high', detected_from: 'headers' };
|
|
269
|
+
}
|
|
270
|
+
if (poweredBy.includes('express') || poweredBy.includes('koa')) {
|
|
271
|
+
// Node.js but unclear which framework
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
if (poweredBy.includes('php') || server.includes('php')) {
|
|
275
|
+
// Could be WordPress or Laravel
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
if (poweredBy.includes('phusion') || poweredBy.includes('passenger')) {
|
|
279
|
+
return { framework: 'rails', confidence: 'medium', detected_from: 'headers' };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Combined detection using multiple sources
|
|
287
|
+
*/
|
|
288
|
+
export function detectFramework(options: {
|
|
289
|
+
html?: string;
|
|
290
|
+
packageJson?: { dependencies?: Record<string, string>; devDependencies?: Record<string, string> };
|
|
291
|
+
headers?: Record<string, string>;
|
|
292
|
+
files?: string[];
|
|
293
|
+
}): FrameworkInfo {
|
|
294
|
+
// Try package.json first (most reliable)
|
|
295
|
+
if (options.packageJson) {
|
|
296
|
+
const result = detectFromPackageJson(options.packageJson);
|
|
297
|
+
if (result) return result;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Try headers
|
|
301
|
+
if (options.headers) {
|
|
302
|
+
const result = detectFromHeaders(options.headers);
|
|
303
|
+
if (result) return result;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Try HTML signatures
|
|
307
|
+
if (options.html) {
|
|
308
|
+
const result = detectFromHtml(options.html);
|
|
309
|
+
if (result) return result;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Check file patterns
|
|
313
|
+
if (options.files) {
|
|
314
|
+
if (options.files.some(f => f.includes('next.config'))) {
|
|
315
|
+
return { framework: 'nextjs', confidence: 'high', detected_from: 'files' };
|
|
316
|
+
}
|
|
317
|
+
if (options.files.some(f => f.includes('nuxt.config'))) {
|
|
318
|
+
return { framework: 'nuxt', confidence: 'high', detected_from: 'files' };
|
|
319
|
+
}
|
|
320
|
+
if (options.files.some(f => f.includes('astro.config'))) {
|
|
321
|
+
return { framework: 'astro', confidence: 'high', detected_from: 'files' };
|
|
322
|
+
}
|
|
323
|
+
if (options.files.some(f => f.includes('svelte.config'))) {
|
|
324
|
+
return { framework: 'sveltekit', confidence: 'high', detected_from: 'files' };
|
|
325
|
+
}
|
|
326
|
+
if (options.files.some(f => f.includes('angular.json'))) {
|
|
327
|
+
return { framework: 'angular', confidence: 'high', detected_from: 'files' };
|
|
328
|
+
}
|
|
329
|
+
if (options.files.some(f => f.includes('gatsby-config'))) {
|
|
330
|
+
return { framework: 'gatsby', confidence: 'high', detected_from: 'files' };
|
|
331
|
+
}
|
|
332
|
+
if (options.files.some(f => f === 'wp-config.php')) {
|
|
333
|
+
return { framework: 'wordpress', confidence: 'high', detected_from: 'files' };
|
|
334
|
+
}
|
|
335
|
+
if (options.files.some(f => f === 'artisan')) {
|
|
336
|
+
return { framework: 'laravel', confidence: 'high', detected_from: 'files' };
|
|
337
|
+
}
|
|
338
|
+
if (options.files.some(f => f === 'manage.py')) {
|
|
339
|
+
return { framework: 'django', confidence: 'high', detected_from: 'files' };
|
|
340
|
+
}
|
|
341
|
+
if (options.files.some(f => f === 'Gemfile') && options.files.some(f => f.includes('config/routes.rb'))) {
|
|
342
|
+
return { framework: 'rails', confidence: 'high', detected_from: 'files' };
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return { framework: 'unknown', confidence: 'low', detected_from: 'html' };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Get human-readable framework name
|
|
351
|
+
*/
|
|
352
|
+
export function getFrameworkDisplayName(framework: Framework): string {
|
|
353
|
+
const names: Record<Framework, string> = {
|
|
354
|
+
react: 'React',
|
|
355
|
+
nextjs: 'Next.js',
|
|
356
|
+
vue: 'Vue.js',
|
|
357
|
+
nuxt: 'Nuxt',
|
|
358
|
+
angular: 'Angular',
|
|
359
|
+
svelte: 'Svelte',
|
|
360
|
+
sveltekit: 'SvelteKit',
|
|
361
|
+
astro: 'Astro',
|
|
362
|
+
remix: 'Remix',
|
|
363
|
+
gatsby: 'Gatsby',
|
|
364
|
+
rails: 'Ruby on Rails',
|
|
365
|
+
laravel: 'Laravel',
|
|
366
|
+
django: 'Django',
|
|
367
|
+
wordpress: 'WordPress',
|
|
368
|
+
unknown: 'Unknown',
|
|
369
|
+
};
|
|
370
|
+
return names[framework];
|
|
371
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework Detection and SEO Recipes
|
|
3
|
+
*
|
|
4
|
+
* Provides framework-specific SEO suggestions for:
|
|
5
|
+
* - React, Next.js, Gatsby, Remix
|
|
6
|
+
* - Vue, Nuxt
|
|
7
|
+
* - Angular
|
|
8
|
+
* - Svelte, SvelteKit
|
|
9
|
+
* - Astro
|
|
10
|
+
* - Ruby on Rails
|
|
11
|
+
* - Laravel, Django
|
|
12
|
+
* - WordPress
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
*
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { detectFramework, getFrameworkIssues } from './frameworks';
|
|
18
|
+
*
|
|
19
|
+
* // Detect framework from HTML
|
|
20
|
+
* const frameworkInfo = detectFramework({ html });
|
|
21
|
+
*
|
|
22
|
+
* // Get framework-specific issues
|
|
23
|
+
* const issues = await getFrameworkIssues(frameworkInfo, html, url);
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
type Framework,
|
|
29
|
+
type FrameworkInfo,
|
|
30
|
+
detectFramework,
|
|
31
|
+
detectFromHtml,
|
|
32
|
+
detectFromPackageJson,
|
|
33
|
+
detectFromHeaders,
|
|
34
|
+
getFrameworkDisplayName,
|
|
35
|
+
} from './detector.js';
|
|
36
|
+
|
|
37
|
+
export {
|
|
38
|
+
type FrameworkCheck,
|
|
39
|
+
type FrameworkRecipe,
|
|
40
|
+
loadRecipe,
|
|
41
|
+
getFrameworkIssues,
|
|
42
|
+
getFrameworkSuggestions,
|
|
43
|
+
getFrameworkChecks,
|
|
44
|
+
getPerformanceTips,
|
|
45
|
+
getSeoChallenges,
|
|
46
|
+
getMigrationPaths,
|
|
47
|
+
getTotalFrameworkChecks,
|
|
48
|
+
getFrameworkSummary,
|
|
49
|
+
embedRecipe,
|
|
50
|
+
} from './suggestion-engine.js';
|
|
51
|
+
|
|
52
|
+
// Convenience function to get all available frameworks
|
|
53
|
+
export const SUPPORTED_FRAMEWORKS = [
|
|
54
|
+
'react',
|
|
55
|
+
'nextjs',
|
|
56
|
+
'vue',
|
|
57
|
+
'nuxt',
|
|
58
|
+
'angular',
|
|
59
|
+
'svelte',
|
|
60
|
+
'sveltekit',
|
|
61
|
+
'astro',
|
|
62
|
+
'remix',
|
|
63
|
+
'gatsby',
|
|
64
|
+
'rails',
|
|
65
|
+
'laravel',
|
|
66
|
+
'django',
|
|
67
|
+
'wordpress',
|
|
68
|
+
] as const;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# Angular SEO Recipe
|
|
2
|
+
# Angular has SSR support via Angular Universal
|
|
3
|
+
|
|
4
|
+
framework: angular
|
|
5
|
+
display_name: Angular
|
|
6
|
+
category: spa
|
|
7
|
+
ssr_default: false
|
|
8
|
+
|
|
9
|
+
seo_challenges:
|
|
10
|
+
- "Client-side rendering by default"
|
|
11
|
+
- "Heavy initial bundle size"
|
|
12
|
+
- "Complex SSR setup (Angular Universal)"
|
|
13
|
+
- "Zone.js can cause performance issues"
|
|
14
|
+
|
|
15
|
+
checks:
|
|
16
|
+
- code: ANGULAR_NO_UNIVERSAL
|
|
17
|
+
severity: error
|
|
18
|
+
title: "Angular app without Universal (SSR)"
|
|
19
|
+
description: "Client-side Angular apps send empty HTML to crawlers."
|
|
20
|
+
impact: "Your content may not be indexed by search engines"
|
|
21
|
+
howToFix: |
|
|
22
|
+
Add Angular Universal for SSR:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
ng add @angular/ssr
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
This adds server-side rendering to your Angular app.
|
|
29
|
+
|
|
30
|
+
- code: ANGULAR_NO_TITLE_SERVICE
|
|
31
|
+
severity: error
|
|
32
|
+
title: "Not using Title/Meta services"
|
|
33
|
+
description: "Angular has built-in services for managing title and meta tags."
|
|
34
|
+
howToFix: |
|
|
35
|
+
Use Title and Meta services:
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { Title, Meta } from '@angular/platform-browser';
|
|
39
|
+
|
|
40
|
+
@Component({...})
|
|
41
|
+
export class ProductComponent {
|
|
42
|
+
constructor(
|
|
43
|
+
private title: Title,
|
|
44
|
+
private meta: Meta
|
|
45
|
+
) {}
|
|
46
|
+
|
|
47
|
+
ngOnInit() {
|
|
48
|
+
this.title.setTitle('Product Name | Your Site');
|
|
49
|
+
this.meta.updateTag({ name: 'description', content: 'Product description' });
|
|
50
|
+
this.meta.updateTag({ property: 'og:title', content: 'Product Name' });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- code: ANGULAR_LAZY_LOAD_MISSING
|
|
56
|
+
severity: warning
|
|
57
|
+
title: "Routes not lazy loaded"
|
|
58
|
+
description: "Loading all modules upfront increases initial bundle size."
|
|
59
|
+
howToFix: |
|
|
60
|
+
Use lazy loading for routes:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
const routes: Routes = [
|
|
64
|
+
{
|
|
65
|
+
path: 'products',
|
|
66
|
+
loadChildren: () => import('./products/products.module')
|
|
67
|
+
.then(m => m.ProductsModule)
|
|
68
|
+
},
|
|
69
|
+
// Or with standalone components
|
|
70
|
+
{
|
|
71
|
+
path: 'about',
|
|
72
|
+
loadComponent: () => import('./about/about.component')
|
|
73
|
+
.then(m => m.AboutComponent)
|
|
74
|
+
}
|
|
75
|
+
];
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- code: ANGULAR_PRERENDER_MISSING
|
|
79
|
+
severity: warning
|
|
80
|
+
title: "Static routes not prerendered"
|
|
81
|
+
description: "Static pages should be prerendered at build time."
|
|
82
|
+
howToFix: |
|
|
83
|
+
Configure prerendering in angular.json:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"projects": {
|
|
88
|
+
"your-app": {
|
|
89
|
+
"architect": {
|
|
90
|
+
"prerender": {
|
|
91
|
+
"options": {
|
|
92
|
+
"routes": ["/", "/about", "/contact"]
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Run: `ng run your-app:prerender`
|
|
102
|
+
|
|
103
|
+
- code: ANGULAR_CHANGE_DETECTION
|
|
104
|
+
severity: notice
|
|
105
|
+
title: "Using default change detection"
|
|
106
|
+
description: "OnPush change detection improves performance significantly."
|
|
107
|
+
howToFix: |
|
|
108
|
+
Use OnPush for components:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
@Component({
|
|
112
|
+
selector: 'app-product',
|
|
113
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
114
|
+
template: `...`
|
|
115
|
+
})
|
|
116
|
+
export class ProductComponent {}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- code: ANGULAR_LARGE_BUNDLE
|
|
120
|
+
severity: warning
|
|
121
|
+
title: "Large initial bundle detected"
|
|
122
|
+
description: "Bundle over 500KB significantly hurts performance."
|
|
123
|
+
howToFix: |
|
|
124
|
+
1. Enable build optimizer:
|
|
125
|
+
```json
|
|
126
|
+
// angular.json
|
|
127
|
+
"optimization": true,
|
|
128
|
+
"buildOptimizer": true
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
2. Analyze bundle:
|
|
132
|
+
```bash
|
|
133
|
+
ng build --stats-json
|
|
134
|
+
npx webpack-bundle-analyzer dist/your-app/stats.json
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
3. Remove unused dependencies
|
|
138
|
+
|
|
139
|
+
- code: ANGULAR_NO_IMAGE_DIRECTIVE
|
|
140
|
+
severity: notice
|
|
141
|
+
title: "Not using NgOptimizedImage"
|
|
142
|
+
description: "Angular 15+ has built-in image optimization."
|
|
143
|
+
howToFix: |
|
|
144
|
+
Use NgOptimizedImage directive:
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { NgOptimizedImage } from '@angular/common';
|
|
148
|
+
|
|
149
|
+
@Component({
|
|
150
|
+
imports: [NgOptimizedImage],
|
|
151
|
+
template: `
|
|
152
|
+
<img ngSrc="/hero.jpg" width="1200" height="630" priority />
|
|
153
|
+
`
|
|
154
|
+
})
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
performance_tips:
|
|
158
|
+
- "Use standalone components to reduce bundle"
|
|
159
|
+
- "Enable OnPush change detection"
|
|
160
|
+
- "Lazy load routes"
|
|
161
|
+
- "Use trackBy with ngFor"
|
|
162
|
+
- "Prerender static pages"
|
|
163
|
+
|
|
164
|
+
migration_paths:
|
|
165
|
+
analog:
|
|
166
|
+
effort: medium
|
|
167
|
+
benefits:
|
|
168
|
+
- "File-based routing"
|
|
169
|
+
- "Full SSR support"
|
|
170
|
+
- "Vite-powered builds"
|
|
171
|
+
guide: "https://analogjs.org/docs/introduction"
|