@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.
Files changed (178) hide show
  1. package/README.md +242 -0
  2. package/dist/analyzer-2CSWIQGD.mjs +6 -0
  3. package/dist/chunk-YNZYHEYM.mjs +774 -0
  4. package/dist/index.d.mts +4012 -0
  5. package/dist/index.d.ts +4012 -0
  6. package/dist/index.js +29672 -0
  7. package/dist/index.mjs +28602 -0
  8. package/package.json +53 -0
  9. package/scripts/build-deno.ts +134 -0
  10. package/src/audit/ai/analyzer.ts +347 -0
  11. package/src/audit/ai/index.ts +29 -0
  12. package/src/audit/ai/prompts/content-analysis.ts +271 -0
  13. package/src/audit/ai/types.ts +179 -0
  14. package/src/audit/checks/additional-checks.ts +439 -0
  15. package/src/audit/checks/ai-citation-worthiness.ts +399 -0
  16. package/src/audit/checks/ai-content-structure.ts +325 -0
  17. package/src/audit/checks/ai-readiness.ts +339 -0
  18. package/src/audit/checks/anchor-text.ts +179 -0
  19. package/src/audit/checks/answer-conciseness.ts +322 -0
  20. package/src/audit/checks/asset-minification.ts +270 -0
  21. package/src/audit/checks/bing-optimization.ts +206 -0
  22. package/src/audit/checks/brand-mention-optimization.ts +349 -0
  23. package/src/audit/checks/caching-headers.ts +305 -0
  24. package/src/audit/checks/canonical-advanced.ts +150 -0
  25. package/src/audit/checks/canonical-domain.ts +196 -0
  26. package/src/audit/checks/citation-quality.ts +358 -0
  27. package/src/audit/checks/client-rendering.ts +542 -0
  28. package/src/audit/checks/color-contrast.ts +342 -0
  29. package/src/audit/checks/content-freshness.ts +170 -0
  30. package/src/audit/checks/content-science.ts +589 -0
  31. package/src/audit/checks/conversion-elements.ts +526 -0
  32. package/src/audit/checks/crawlability.ts +220 -0
  33. package/src/audit/checks/directory-listing.ts +172 -0
  34. package/src/audit/checks/dom-analysis.ts +191 -0
  35. package/src/audit/checks/dom-size.ts +246 -0
  36. package/src/audit/checks/duplicate-content.ts +194 -0
  37. package/src/audit/checks/eeat-signals.ts +990 -0
  38. package/src/audit/checks/entity-seo.ts +396 -0
  39. package/src/audit/checks/featured-snippet.ts +473 -0
  40. package/src/audit/checks/freshness-signals.ts +443 -0
  41. package/src/audit/checks/funnel-intent.ts +463 -0
  42. package/src/audit/checks/hreflang.ts +174 -0
  43. package/src/audit/checks/html-compliance.ts +302 -0
  44. package/src/audit/checks/image-dimensions.ts +167 -0
  45. package/src/audit/checks/images.ts +160 -0
  46. package/src/audit/checks/indexnow.ts +275 -0
  47. package/src/audit/checks/interactive-tools.ts +475 -0
  48. package/src/audit/checks/internal-link-graph.ts +436 -0
  49. package/src/audit/checks/keyword-analysis.ts +239 -0
  50. package/src/audit/checks/keyword-cannibalization.ts +385 -0
  51. package/src/audit/checks/keyword-placement.ts +471 -0
  52. package/src/audit/checks/links.ts +203 -0
  53. package/src/audit/checks/llms-txt.ts +224 -0
  54. package/src/audit/checks/local-seo.ts +296 -0
  55. package/src/audit/checks/mobile.ts +167 -0
  56. package/src/audit/checks/modern-images.ts +226 -0
  57. package/src/audit/checks/navboost-signals.ts +395 -0
  58. package/src/audit/checks/on-page.ts +209 -0
  59. package/src/audit/checks/page-resources.ts +285 -0
  60. package/src/audit/checks/pagination.ts +180 -0
  61. package/src/audit/checks/performance.ts +153 -0
  62. package/src/audit/checks/platform-presence.ts +580 -0
  63. package/src/audit/checks/redirect-analysis.ts +153 -0
  64. package/src/audit/checks/redirect-chain.ts +389 -0
  65. package/src/audit/checks/resource-hints.ts +420 -0
  66. package/src/audit/checks/responsive-css.ts +247 -0
  67. package/src/audit/checks/responsive-images.ts +396 -0
  68. package/src/audit/checks/review-ecosystem.ts +415 -0
  69. package/src/audit/checks/robots-validation.ts +373 -0
  70. package/src/audit/checks/security-headers.ts +172 -0
  71. package/src/audit/checks/security.ts +144 -0
  72. package/src/audit/checks/serp-preview.ts +251 -0
  73. package/src/audit/checks/site-maturity.ts +444 -0
  74. package/src/audit/checks/social-meta.test.ts +275 -0
  75. package/src/audit/checks/social-meta.ts +134 -0
  76. package/src/audit/checks/soft-404.ts +151 -0
  77. package/src/audit/checks/structured-data.ts +238 -0
  78. package/src/audit/checks/tech-detection.ts +496 -0
  79. package/src/audit/checks/topical-clusters.ts +435 -0
  80. package/src/audit/checks/tracker-bloat.ts +462 -0
  81. package/src/audit/checks/tracking-verification.test.ts +371 -0
  82. package/src/audit/checks/tracking-verification.ts +636 -0
  83. package/src/audit/checks/url-safety.ts +682 -0
  84. package/src/audit/deno-entry.ts +66 -0
  85. package/src/audit/discovery/index.ts +15 -0
  86. package/src/audit/discovery/link-crawler.ts +232 -0
  87. package/src/audit/discovery/repo-routes.ts +347 -0
  88. package/src/audit/engine.ts +620 -0
  89. package/src/audit/fixes/index.ts +209 -0
  90. package/src/audit/fixes/social-meta-fixes.test.ts +329 -0
  91. package/src/audit/fixes/social-meta-fixes.ts +463 -0
  92. package/src/audit/index.ts +74 -0
  93. package/src/audit/runner.test.ts +299 -0
  94. package/src/audit/runner.ts +130 -0
  95. package/src/audit/types.ts +1953 -0
  96. package/src/content/featured-snippet.ts +367 -0
  97. package/src/content/generator.test.ts +534 -0
  98. package/src/content/generator.ts +501 -0
  99. package/src/content/headline.ts +317 -0
  100. package/src/content/index.ts +62 -0
  101. package/src/content/intent.ts +258 -0
  102. package/src/content/keyword-density.ts +349 -0
  103. package/src/content/readability.ts +262 -0
  104. package/src/executor.ts +336 -0
  105. package/src/fixer.ts +416 -0
  106. package/src/frameworks/detector.test.ts +248 -0
  107. package/src/frameworks/detector.ts +371 -0
  108. package/src/frameworks/index.ts +68 -0
  109. package/src/frameworks/recipes/angular.yaml +171 -0
  110. package/src/frameworks/recipes/astro.yaml +206 -0
  111. package/src/frameworks/recipes/django.yaml +180 -0
  112. package/src/frameworks/recipes/laravel.yaml +137 -0
  113. package/src/frameworks/recipes/nextjs.yaml +268 -0
  114. package/src/frameworks/recipes/nuxt.yaml +175 -0
  115. package/src/frameworks/recipes/rails.yaml +188 -0
  116. package/src/frameworks/recipes/react.yaml +202 -0
  117. package/src/frameworks/recipes/sveltekit.yaml +154 -0
  118. package/src/frameworks/recipes/vue.yaml +137 -0
  119. package/src/frameworks/recipes/wordpress.yaml +209 -0
  120. package/src/frameworks/suggestion-engine.ts +320 -0
  121. package/src/geo/geo-content.test.ts +305 -0
  122. package/src/geo/geo-content.ts +266 -0
  123. package/src/geo/geo-history.test.ts +473 -0
  124. package/src/geo/geo-history.ts +433 -0
  125. package/src/geo/geo-tracker.test.ts +359 -0
  126. package/src/geo/geo-tracker.ts +411 -0
  127. package/src/geo/index.ts +10 -0
  128. package/src/git/commit-helper.test.ts +261 -0
  129. package/src/git/commit-helper.ts +329 -0
  130. package/src/git/index.ts +12 -0
  131. package/src/git/pr-helper.test.ts +284 -0
  132. package/src/git/pr-helper.ts +307 -0
  133. package/src/index.ts +66 -0
  134. package/src/keywords/ai-keyword-engine.ts +1062 -0
  135. package/src/keywords/ai-summarizer.ts +387 -0
  136. package/src/keywords/ci-mode.ts +555 -0
  137. package/src/keywords/engine.ts +359 -0
  138. package/src/keywords/index.ts +151 -0
  139. package/src/keywords/llm-judge.ts +357 -0
  140. package/src/keywords/nlp-analysis.ts +706 -0
  141. package/src/keywords/prioritizer.ts +295 -0
  142. package/src/keywords/site-crawler.ts +342 -0
  143. package/src/keywords/sources/autocomplete.ts +139 -0
  144. package/src/keywords/sources/competitive-search.ts +450 -0
  145. package/src/keywords/sources/competitor-analysis.ts +374 -0
  146. package/src/keywords/sources/dataforseo.ts +206 -0
  147. package/src/keywords/sources/free-sources.ts +294 -0
  148. package/src/keywords/sources/gsc.ts +123 -0
  149. package/src/keywords/topic-grouping.ts +327 -0
  150. package/src/keywords/types.ts +144 -0
  151. package/src/keywords/wizard.ts +457 -0
  152. package/src/loader.ts +40 -0
  153. package/src/reports/index.ts +7 -0
  154. package/src/reports/report-generator.test.ts +293 -0
  155. package/src/reports/report-generator.ts +713 -0
  156. package/src/scheduler/alerts.test.ts +458 -0
  157. package/src/scheduler/alerts.ts +328 -0
  158. package/src/scheduler/index.ts +8 -0
  159. package/src/scheduler/scheduled-audit.test.ts +377 -0
  160. package/src/scheduler/scheduled-audit.ts +149 -0
  161. package/src/test/integration-test.ts +325 -0
  162. package/src/tools/analyzer.ts +373 -0
  163. package/src/tools/crawl.ts +293 -0
  164. package/src/tools/files.ts +301 -0
  165. package/src/tools/h1-fixer.ts +249 -0
  166. package/src/tools/index.ts +67 -0
  167. package/src/tracking/github-action.ts +326 -0
  168. package/src/tracking/google-analytics.ts +265 -0
  169. package/src/tracking/index.ts +45 -0
  170. package/src/tracking/report-generator.ts +386 -0
  171. package/src/tracking/search-console.ts +335 -0
  172. package/src/types.ts +134 -0
  173. package/src/utils/http.ts +302 -0
  174. package/src/wasm-adapter.ts +297 -0
  175. package/src/wasm-entry.ts +14 -0
  176. package/tsconfig.json +17 -0
  177. package/tsup.wasm.config.ts +26 -0
  178. 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"