@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,268 @@
|
|
|
1
|
+
# Next.js SEO Recipe
|
|
2
|
+
# Next.js has excellent SEO by default, but there are still common mistakes
|
|
3
|
+
|
|
4
|
+
framework: nextjs
|
|
5
|
+
display_name: Next.js
|
|
6
|
+
category: meta-framework
|
|
7
|
+
base_framework: react
|
|
8
|
+
ssr_default: true
|
|
9
|
+
|
|
10
|
+
seo_strengths:
|
|
11
|
+
- "Server-side rendering by default"
|
|
12
|
+
- "Static site generation support"
|
|
13
|
+
- "Built-in Image component with optimization"
|
|
14
|
+
- "Automatic code splitting"
|
|
15
|
+
- "File-based routing"
|
|
16
|
+
- "Built-in metadata API (App Router)"
|
|
17
|
+
|
|
18
|
+
checks:
|
|
19
|
+
# App Router specific
|
|
20
|
+
- code: NEXTJS_NO_METADATA_API
|
|
21
|
+
severity: warning
|
|
22
|
+
title: "Not using Next.js Metadata API"
|
|
23
|
+
description: "Next.js 13+ has a built-in Metadata API that's better than react-helmet."
|
|
24
|
+
howToFix: |
|
|
25
|
+
Use the Metadata API in your layout or page:
|
|
26
|
+
|
|
27
|
+
```tsx
|
|
28
|
+
// app/page.tsx
|
|
29
|
+
import { Metadata } from 'next';
|
|
30
|
+
|
|
31
|
+
export const metadata: Metadata = {
|
|
32
|
+
title: 'Page Title',
|
|
33
|
+
description: 'Page description',
|
|
34
|
+
openGraph: {
|
|
35
|
+
title: 'OG Title',
|
|
36
|
+
description: 'OG Description',
|
|
37
|
+
images: ['/og-image.jpg'],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// For dynamic metadata
|
|
42
|
+
export async function generateMetadata({ params }): Promise<Metadata> {
|
|
43
|
+
const product = await getProduct(params.id);
|
|
44
|
+
return {
|
|
45
|
+
title: product.name,
|
|
46
|
+
description: product.description,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
app_router_only: true
|
|
51
|
+
|
|
52
|
+
- code: NEXTJS_GENERATESTATICPARAMS_MISSING
|
|
53
|
+
severity: warning
|
|
54
|
+
title: "Dynamic routes without generateStaticParams"
|
|
55
|
+
description: "Dynamic routes like [id] should pre-generate static pages for SEO."
|
|
56
|
+
howToFix: |
|
|
57
|
+
Add generateStaticParams to pre-render dynamic pages:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
// app/products/[id]/page.tsx
|
|
61
|
+
export async function generateStaticParams() {
|
|
62
|
+
const products = await getProducts();
|
|
63
|
+
return products.map((product) => ({
|
|
64
|
+
id: product.id.toString(),
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
app_router_only: true
|
|
69
|
+
|
|
70
|
+
# Pages Router specific
|
|
71
|
+
- code: NEXTJS_NO_GETSTATICPROPS
|
|
72
|
+
severity: warning
|
|
73
|
+
title: "Pages without getStaticProps/getServerSideProps"
|
|
74
|
+
description: "Pages fetching data client-side miss SEO benefits of SSR/SSG."
|
|
75
|
+
howToFix: |
|
|
76
|
+
Move data fetching to getStaticProps (for static) or getServerSideProps (for dynamic):
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// pages/products.tsx
|
|
80
|
+
export async function getStaticProps() {
|
|
81
|
+
const products = await fetchProducts();
|
|
82
|
+
return {
|
|
83
|
+
props: { products },
|
|
84
|
+
revalidate: 3600, // ISR: regenerate every hour
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
pages_router_only: true
|
|
89
|
+
|
|
90
|
+
# Common issues
|
|
91
|
+
- code: NEXTJS_IMAGE_NOT_USED
|
|
92
|
+
severity: warning
|
|
93
|
+
title: "Using <img> instead of next/image"
|
|
94
|
+
description: "The next/image component provides automatic optimization, lazy loading, and prevents CLS."
|
|
95
|
+
howToFix: |
|
|
96
|
+
Replace <img> with next/image:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import Image from 'next/image';
|
|
100
|
+
|
|
101
|
+
// Before
|
|
102
|
+
<img src="/hero.jpg" alt="Hero" />
|
|
103
|
+
|
|
104
|
+
// After
|
|
105
|
+
<Image
|
|
106
|
+
src="/hero.jpg"
|
|
107
|
+
alt="Hero"
|
|
108
|
+
width={1200}
|
|
109
|
+
height={630}
|
|
110
|
+
priority // for above-the-fold images
|
|
111
|
+
/>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- code: NEXTJS_NO_PRIORITY_IMAGE
|
|
115
|
+
severity: notice
|
|
116
|
+
title: "LCP image missing priority prop"
|
|
117
|
+
description: "The largest image above the fold should have priority={true} for faster LCP."
|
|
118
|
+
howToFix: |
|
|
119
|
+
Add priority to your hero/LCP image:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
<Image
|
|
123
|
+
src="/hero.jpg"
|
|
124
|
+
alt="Hero"
|
|
125
|
+
width={1200}
|
|
126
|
+
height={630}
|
|
127
|
+
priority
|
|
128
|
+
/>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
- code: NEXTJS_LINK_NO_PREFETCH
|
|
132
|
+
severity: notice
|
|
133
|
+
title: "Link without prefetch consideration"
|
|
134
|
+
description: "Next.js prefetches all links by default, which can waste bandwidth."
|
|
135
|
+
howToFix: |
|
|
136
|
+
Disable prefetch for less important links:
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
<Link href="/terms" prefetch={false}>
|
|
140
|
+
Terms of Service
|
|
141
|
+
</Link>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
- code: NEXTJS_NO_ROBOTS_TXT
|
|
145
|
+
severity: warning
|
|
146
|
+
title: "Missing robots.txt configuration"
|
|
147
|
+
description: "Next.js can generate robots.txt automatically."
|
|
148
|
+
howToFix: |
|
|
149
|
+
App Router - create app/robots.ts:
|
|
150
|
+
```tsx
|
|
151
|
+
import { MetadataRoute } from 'next';
|
|
152
|
+
|
|
153
|
+
export default function robots(): MetadataRoute.Robots {
|
|
154
|
+
return {
|
|
155
|
+
rules: {
|
|
156
|
+
userAgent: '*',
|
|
157
|
+
allow: '/',
|
|
158
|
+
disallow: '/api/',
|
|
159
|
+
},
|
|
160
|
+
sitemap: 'https://yoursite.com/sitemap.xml',
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Pages Router - create public/robots.txt or use next-sitemap package.
|
|
166
|
+
|
|
167
|
+
- code: NEXTJS_NO_SITEMAP
|
|
168
|
+
severity: warning
|
|
169
|
+
title: "Missing sitemap configuration"
|
|
170
|
+
description: "Next.js can generate sitemaps automatically."
|
|
171
|
+
howToFix: |
|
|
172
|
+
App Router - create app/sitemap.ts:
|
|
173
|
+
```tsx
|
|
174
|
+
import { MetadataRoute } from 'next';
|
|
175
|
+
|
|
176
|
+
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
|
|
177
|
+
const products = await getProducts();
|
|
178
|
+
|
|
179
|
+
return [
|
|
180
|
+
{ url: 'https://yoursite.com', lastModified: new Date() },
|
|
181
|
+
{ url: 'https://yoursite.com/about', lastModified: new Date() },
|
|
182
|
+
...products.map((product) => ({
|
|
183
|
+
url: `https://yoursite.com/products/${product.id}`,
|
|
184
|
+
lastModified: product.updatedAt,
|
|
185
|
+
})),
|
|
186
|
+
];
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- code: NEXTJS_USE_CLIENT_OVERUSE
|
|
191
|
+
severity: warning
|
|
192
|
+
title: "Overusing 'use client' directive"
|
|
193
|
+
description: "Too many client components reduces SSR benefits."
|
|
194
|
+
howToFix: |
|
|
195
|
+
Keep 'use client' at the leaf level:
|
|
196
|
+
|
|
197
|
+
```tsx
|
|
198
|
+
// Bad - entire page is client-rendered
|
|
199
|
+
'use client';
|
|
200
|
+
export default function Page() { ... }
|
|
201
|
+
|
|
202
|
+
// Good - only interactive parts are client
|
|
203
|
+
// page.tsx (Server Component)
|
|
204
|
+
import ClientButton from './ClientButton';
|
|
205
|
+
export default function Page() {
|
|
206
|
+
return <div>
|
|
207
|
+
<h1>Server rendered content</h1>
|
|
208
|
+
<ClientButton /> {/* Only this is client */}
|
|
209
|
+
</div>
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- code: NEXTJS_FONT_NOT_OPTIMIZED
|
|
214
|
+
severity: notice
|
|
215
|
+
title: "Not using next/font for fonts"
|
|
216
|
+
description: "next/font automatically optimizes fonts and prevents CLS."
|
|
217
|
+
howToFix: |
|
|
218
|
+
Use next/font for Google Fonts or local fonts:
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
import { Inter } from 'next/font/google';
|
|
222
|
+
|
|
223
|
+
const inter = Inter({ subsets: ['latin'] });
|
|
224
|
+
|
|
225
|
+
export default function RootLayout({ children }) {
|
|
226
|
+
return (
|
|
227
|
+
<html lang="en" className={inter.className}>
|
|
228
|
+
<body>{children}</body>
|
|
229
|
+
</html>
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
- code: NEXTJS_NO_TRAILING_SLASH_CONFIG
|
|
235
|
+
severity: notice
|
|
236
|
+
title: "Inconsistent trailing slash handling"
|
|
237
|
+
description: "Inconsistent URLs can cause duplicate content issues."
|
|
238
|
+
howToFix: |
|
|
239
|
+
Configure trailingSlash in next.config.js:
|
|
240
|
+
|
|
241
|
+
```js
|
|
242
|
+
module.exports = {
|
|
243
|
+
trailingSlash: true, // or false
|
|
244
|
+
};
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
performance_tips:
|
|
248
|
+
- "Use React Server Components for static content"
|
|
249
|
+
- "Add priority to LCP images"
|
|
250
|
+
- "Use next/font for fonts"
|
|
251
|
+
- "Enable ISR for frequently updated pages"
|
|
252
|
+
- "Use Route Handlers instead of API routes for server-only logic"
|
|
253
|
+
|
|
254
|
+
structured_data:
|
|
255
|
+
- type: "JSON-LD in metadata"
|
|
256
|
+
example: |
|
|
257
|
+
```tsx
|
|
258
|
+
export const metadata: Metadata = {
|
|
259
|
+
other: {
|
|
260
|
+
'script:ld+json': JSON.stringify({
|
|
261
|
+
'@context': 'https://schema.org',
|
|
262
|
+
'@type': 'Organization',
|
|
263
|
+
name: 'Your Company',
|
|
264
|
+
url: 'https://yoursite.com',
|
|
265
|
+
}),
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
```
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# Nuxt SEO Recipe
|
|
2
|
+
# Nuxt has excellent SEO capabilities, similar to Next.js for Vue
|
|
3
|
+
|
|
4
|
+
framework: nuxt
|
|
5
|
+
display_name: Nuxt
|
|
6
|
+
category: meta-framework
|
|
7
|
+
base_framework: vue
|
|
8
|
+
ssr_default: true
|
|
9
|
+
|
|
10
|
+
seo_strengths:
|
|
11
|
+
- "Universal rendering (SSR) by default"
|
|
12
|
+
- "Static site generation support"
|
|
13
|
+
- "Built-in useSeoMeta composable"
|
|
14
|
+
- "Automatic code splitting"
|
|
15
|
+
- "File-based routing"
|
|
16
|
+
- "NuxtImage for image optimization"
|
|
17
|
+
|
|
18
|
+
checks:
|
|
19
|
+
- code: NUXT_NO_USESEOMETA
|
|
20
|
+
severity: warning
|
|
21
|
+
title: "Not using useSeoMeta composable"
|
|
22
|
+
description: "Nuxt 3 has built-in SEO meta composables that are cleaner than useHead."
|
|
23
|
+
howToFix: |
|
|
24
|
+
Use useSeoMeta for SEO tags:
|
|
25
|
+
|
|
26
|
+
```vue
|
|
27
|
+
<script setup>
|
|
28
|
+
useSeoMeta({
|
|
29
|
+
title: 'Page Title',
|
|
30
|
+
description: 'Page description',
|
|
31
|
+
ogTitle: 'OG Title',
|
|
32
|
+
ogDescription: 'OG Description',
|
|
33
|
+
ogImage: '/og-image.jpg',
|
|
34
|
+
twitterCard: 'summary_large_image',
|
|
35
|
+
});
|
|
36
|
+
</script>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- code: NUXT_USEFETCH_CLIENT_ONLY
|
|
40
|
+
severity: warning
|
|
41
|
+
title: "Data fetching only on client side"
|
|
42
|
+
description: "Using onMounted for data fetching bypasses SSR benefits."
|
|
43
|
+
howToFix: |
|
|
44
|
+
Use useFetch or useAsyncData instead:
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<script setup>
|
|
48
|
+
// Bad - client only
|
|
49
|
+
const data = ref(null);
|
|
50
|
+
onMounted(async () => {
|
|
51
|
+
data.value = await $fetch('/api/data');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Good - SSR friendly
|
|
55
|
+
const { data } = await useFetch('/api/data');
|
|
56
|
+
</script>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- code: NUXT_NO_NUXT_IMAGE
|
|
60
|
+
severity: warning
|
|
61
|
+
title: "Not using NuxtImage for images"
|
|
62
|
+
description: "@nuxt/image provides automatic optimization and responsive images."
|
|
63
|
+
howToFix: |
|
|
64
|
+
Install and use NuxtImage:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx nuxi module add image
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```vue
|
|
71
|
+
<template>
|
|
72
|
+
<NuxtImg
|
|
73
|
+
src="/hero.jpg"
|
|
74
|
+
alt="Hero"
|
|
75
|
+
width="1200"
|
|
76
|
+
height="630"
|
|
77
|
+
preload
|
|
78
|
+
/>
|
|
79
|
+
</template>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- code: NUXT_NO_ROBOTS
|
|
83
|
+
severity: warning
|
|
84
|
+
title: "Missing robots.txt configuration"
|
|
85
|
+
description: "Use @nuxtjs/robots module for robots.txt."
|
|
86
|
+
howToFix: |
|
|
87
|
+
Install and configure robots module:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install @nuxtjs/robots
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
// nuxt.config.ts
|
|
95
|
+
export default defineNuxtConfig({
|
|
96
|
+
modules: ['@nuxtjs/robots'],
|
|
97
|
+
robots: {
|
|
98
|
+
rules: [
|
|
99
|
+
{ UserAgent: '*', Allow: '/' },
|
|
100
|
+
{ UserAgent: '*', Disallow: '/api/' },
|
|
101
|
+
],
|
|
102
|
+
sitemap: 'https://yoursite.com/sitemap.xml',
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- code: NUXT_NO_SITEMAP
|
|
108
|
+
severity: warning
|
|
109
|
+
title: "Missing sitemap configuration"
|
|
110
|
+
description: "Use @nuxtjs/sitemap module for automatic sitemap."
|
|
111
|
+
howToFix: |
|
|
112
|
+
Install sitemap module:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
npm install @nuxtjs/sitemap
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
// nuxt.config.ts
|
|
120
|
+
export default defineNuxtConfig({
|
|
121
|
+
modules: ['@nuxtjs/sitemap'],
|
|
122
|
+
sitemap: {
|
|
123
|
+
hostname: 'https://yoursite.com',
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
- code: NUXT_SPA_MODE
|
|
129
|
+
severity: error
|
|
130
|
+
title: "Nuxt running in SPA mode"
|
|
131
|
+
description: "SPA mode disables SSR, losing SEO benefits."
|
|
132
|
+
howToFix: |
|
|
133
|
+
Remove ssr: false from nuxt.config.ts:
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
export default defineNuxtConfig({
|
|
137
|
+
// Remove this line
|
|
138
|
+
// ssr: false,
|
|
139
|
+
});
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
If you need client-only components, use <ClientOnly>:
|
|
143
|
+
```vue
|
|
144
|
+
<ClientOnly>
|
|
145
|
+
<BrowserOnlyWidget />
|
|
146
|
+
</ClientOnly>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
- code: NUXT_NO_PRERENDER
|
|
150
|
+
severity: notice
|
|
151
|
+
title: "Static pages not pre-rendered"
|
|
152
|
+
description: "Static pages could be pre-rendered for better performance."
|
|
153
|
+
howToFix: |
|
|
154
|
+
Enable route prerendering in nuxt.config.ts:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
export default defineNuxtConfig({
|
|
158
|
+
routeRules: {
|
|
159
|
+
'/about': { prerender: true },
|
|
160
|
+
'/blog/**': { prerender: true },
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Or generate a fully static site:
|
|
166
|
+
```bash
|
|
167
|
+
nuxt generate
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
performance_tips:
|
|
171
|
+
- "Use useLazyFetch for non-critical data"
|
|
172
|
+
- "Add preload prop to NuxtImg for LCP images"
|
|
173
|
+
- "Use Nuxt DevTools to identify performance issues"
|
|
174
|
+
- "Configure image provider for CDN delivery"
|
|
175
|
+
- "Use route rules for caching strategies"
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Ruby on Rails SEO Recipe
|
|
2
|
+
# Rails is server-rendered by default, excellent for SEO
|
|
3
|
+
|
|
4
|
+
framework: rails
|
|
5
|
+
display_name: Ruby on Rails
|
|
6
|
+
category: server-rendered
|
|
7
|
+
ssr_default: true
|
|
8
|
+
|
|
9
|
+
seo_strengths:
|
|
10
|
+
- "Server-rendered HTML by default"
|
|
11
|
+
- "Turbo for fast navigation without SPA"
|
|
12
|
+
- "Strong conventions reduce mistakes"
|
|
13
|
+
- "Mature gem ecosystem"
|
|
14
|
+
|
|
15
|
+
checks:
|
|
16
|
+
- code: RAILS_NO_META_TAGS_GEM
|
|
17
|
+
severity: warning
|
|
18
|
+
title: "Not using meta-tags gem"
|
|
19
|
+
description: "The meta-tags gem provides a clean API for SEO tags."
|
|
20
|
+
howToFix: |
|
|
21
|
+
Add meta-tags to Gemfile:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
gem 'meta-tags'
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Run `bundle install`, then:
|
|
28
|
+
|
|
29
|
+
```ruby
|
|
30
|
+
# app/views/layouts/application.html.erb
|
|
31
|
+
<head>
|
|
32
|
+
<%= display_meta_tags site: 'Your Site' %>
|
|
33
|
+
</head>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
# In controllers or views
|
|
38
|
+
set_meta_tags title: 'Page Title',
|
|
39
|
+
description: 'Page description',
|
|
40
|
+
keywords: 'seo, rails'
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- code: RAILS_NO_SITEMAP_GEM
|
|
44
|
+
severity: warning
|
|
45
|
+
title: "Not using sitemap_generator gem"
|
|
46
|
+
description: "sitemap_generator creates SEO-friendly sitemaps automatically."
|
|
47
|
+
howToFix: |
|
|
48
|
+
Add to Gemfile:
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
gem 'sitemap_generator'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Generate config:
|
|
55
|
+
```bash
|
|
56
|
+
rails sitemap:install
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Configure config/sitemap.rb:
|
|
60
|
+
```ruby
|
|
61
|
+
SitemapGenerator::Sitemap.default_host = "https://yoursite.com"
|
|
62
|
+
|
|
63
|
+
SitemapGenerator::Sitemap.create do
|
|
64
|
+
add '/about', changefreq: 'monthly'
|
|
65
|
+
Product.find_each do |product|
|
|
66
|
+
add product_path(product), lastmod: product.updated_at
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Generate: `rails sitemap:refresh`
|
|
72
|
+
|
|
73
|
+
- code: RAILS_NO_CANONICAL
|
|
74
|
+
severity: warning
|
|
75
|
+
title: "Missing canonical URLs"
|
|
76
|
+
description: "Rails apps with multiple URLs for same content need canonicals."
|
|
77
|
+
howToFix: |
|
|
78
|
+
Add canonical tag in layout:
|
|
79
|
+
|
|
80
|
+
```erb
|
|
81
|
+
<!-- Using meta-tags gem -->
|
|
82
|
+
<%= display_meta_tags canonical: request.original_url %>
|
|
83
|
+
|
|
84
|
+
<!-- Or manually -->
|
|
85
|
+
<link rel="canonical" href="<%= request.original_url.split('?').first %>" />
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- code: RAILS_TURBO_BREAKS_CRAWLERS
|
|
89
|
+
severity: notice
|
|
90
|
+
title: "Turbo may affect JavaScript-dependent crawlers"
|
|
91
|
+
description: "Turbo Drive updates pages via JavaScript, which some crawlers may miss."
|
|
92
|
+
howToFix: |
|
|
93
|
+
For SEO-critical pages, ensure content is in initial HTML.
|
|
94
|
+
|
|
95
|
+
For dynamic content, add data-turbo="false":
|
|
96
|
+
```erb
|
|
97
|
+
<%= link_to 'Critical Page', critical_path, data: { turbo: false } %>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Or disable for specific pages:
|
|
101
|
+
```erb
|
|
102
|
+
<meta name="turbo-visit-control" content="reload">
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
- code: RAILS_NO_JSON_LD
|
|
106
|
+
severity: notice
|
|
107
|
+
title: "No structured data detected"
|
|
108
|
+
description: "Add JSON-LD for rich snippets."
|
|
109
|
+
howToFix: |
|
|
110
|
+
Create a helper:
|
|
111
|
+
|
|
112
|
+
```ruby
|
|
113
|
+
# app/helpers/seo_helper.rb
|
|
114
|
+
module SeoHelper
|
|
115
|
+
def json_ld(data)
|
|
116
|
+
content_tag :script, data.to_json.html_safe,
|
|
117
|
+
type: 'application/ld+json'
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Use in views:
|
|
123
|
+
```erb
|
|
124
|
+
<%= json_ld({
|
|
125
|
+
"@context" => "https://schema.org",
|
|
126
|
+
"@type" => "Product",
|
|
127
|
+
"name" => @product.name,
|
|
128
|
+
"description" => @product.description
|
|
129
|
+
}) %>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- code: RAILS_SLOW_QUERIES
|
|
133
|
+
severity: warning
|
|
134
|
+
title: "N+1 queries may slow pages"
|
|
135
|
+
description: "Slow pages hurt SEO. Use includes/eager_load."
|
|
136
|
+
howToFix: |
|
|
137
|
+
Use bullet gem to detect N+1:
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
# Gemfile
|
|
141
|
+
gem 'bullet', group: 'development'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Fix with includes:
|
|
145
|
+
```ruby
|
|
146
|
+
# Before (N+1)
|
|
147
|
+
@posts = Post.all
|
|
148
|
+
@posts.each { |p| p.author.name }
|
|
149
|
+
|
|
150
|
+
# After (2 queries)
|
|
151
|
+
@posts = Post.includes(:author).all
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
- code: RAILS_NO_CACHING
|
|
155
|
+
severity: notice
|
|
156
|
+
title: "Page caching not configured"
|
|
157
|
+
description: "Rails caching can dramatically improve response times."
|
|
158
|
+
howToFix: |
|
|
159
|
+
Enable caching in production:
|
|
160
|
+
|
|
161
|
+
```ruby
|
|
162
|
+
# config/environments/production.rb
|
|
163
|
+
config.action_controller.perform_caching = true
|
|
164
|
+
config.cache_store = :redis_cache_store, { url: ENV['REDIS_URL'] }
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Use fragment caching:
|
|
168
|
+
```erb
|
|
169
|
+
<% cache @product do %>
|
|
170
|
+
<%= render @product %>
|
|
171
|
+
<% end %>
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
performance_tips:
|
|
175
|
+
- "Use Russian doll caching for nested views"
|
|
176
|
+
- "Enable asset fingerprinting and CDN"
|
|
177
|
+
- "Use Turbo for SPA-like experience"
|
|
178
|
+
- "Profile with rack-mini-profiler"
|
|
179
|
+
- "Use Postgres for full-text search instead of LIKE"
|
|
180
|
+
|
|
181
|
+
recommended_gems:
|
|
182
|
+
seo:
|
|
183
|
+
- "meta-tags - SEO tag management"
|
|
184
|
+
- "sitemap_generator - XML sitemaps"
|
|
185
|
+
performance:
|
|
186
|
+
- "bullet - N+1 detection"
|
|
187
|
+
- "rack-mini-profiler - Performance profiling"
|
|
188
|
+
- "bootsnap - Faster boot times"
|