@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,202 @@
|
|
|
1
|
+
# React SEO Recipe
|
|
2
|
+
# Client-side rendering frameworks are notoriously problematic for SEO
|
|
3
|
+
|
|
4
|
+
framework: react
|
|
5
|
+
display_name: React
|
|
6
|
+
category: spa
|
|
7
|
+
ssr_default: false
|
|
8
|
+
|
|
9
|
+
seo_challenges:
|
|
10
|
+
- "Client-side rendering means empty HTML for crawlers"
|
|
11
|
+
- "Dynamic content invisible to search engines"
|
|
12
|
+
- "Slow First Contentful Paint"
|
|
13
|
+
- "No server-side meta tag generation"
|
|
14
|
+
- "JavaScript-dependent routing breaks crawlability"
|
|
15
|
+
|
|
16
|
+
checks:
|
|
17
|
+
# Critical - these will severely hurt SEO
|
|
18
|
+
- code: REACT_CSR_DETECTED
|
|
19
|
+
severity: error
|
|
20
|
+
title: "Client-side rendered React app detected"
|
|
21
|
+
description: "React apps without SSR send empty HTML to crawlers. Search engines may not index your content."
|
|
22
|
+
impact: "Critical - your content is likely invisible to Google"
|
|
23
|
+
howToFix: |
|
|
24
|
+
Option 1: Migrate to Next.js for automatic SSR/SSG
|
|
25
|
+
Option 2: Add prerendering with react-snap or prerender.io
|
|
26
|
+
Option 3: Use react-helmet-async with a prerender service
|
|
27
|
+
detection:
|
|
28
|
+
html_patterns:
|
|
29
|
+
- '<div id="root"></div>'
|
|
30
|
+
- '<div id="app"></div>'
|
|
31
|
+
missing_content: true # Body has minimal text content
|
|
32
|
+
|
|
33
|
+
- code: REACT_NO_SSR_META
|
|
34
|
+
severity: error
|
|
35
|
+
title: "Meta tags not server-rendered"
|
|
36
|
+
description: "Title and meta description are set client-side and won't be seen by crawlers."
|
|
37
|
+
impact: "Search results will show wrong or missing title/description"
|
|
38
|
+
howToFix: |
|
|
39
|
+
For Vite/CRA: Use a prerender plugin or migrate to Next.js
|
|
40
|
+
|
|
41
|
+
Quick fix with vite-plugin-ssr:
|
|
42
|
+
```tsx
|
|
43
|
+
// pages/+Head.tsx
|
|
44
|
+
export function Head() {
|
|
45
|
+
return <>
|
|
46
|
+
<title>Your Title</title>
|
|
47
|
+
<meta name="description" content="Your description" />
|
|
48
|
+
</>
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
detection:
|
|
52
|
+
title_in_js: true
|
|
53
|
+
title_not_in_html: true
|
|
54
|
+
|
|
55
|
+
- code: REACT_HYDRATION_MISMATCH
|
|
56
|
+
severity: warning
|
|
57
|
+
title: "Hydration mismatch detected"
|
|
58
|
+
description: "Server HTML differs from client render, causing flicker and potential SEO issues."
|
|
59
|
+
impact: "Can cause content to briefly disappear, confusing crawlers"
|
|
60
|
+
howToFix: |
|
|
61
|
+
Common causes:
|
|
62
|
+
1. Using Date.now() or Math.random() in render
|
|
63
|
+
2. Browser-only APIs like window.localStorage
|
|
64
|
+
3. Different data on server vs client
|
|
65
|
+
|
|
66
|
+
Fix: Use useEffect for browser-only code:
|
|
67
|
+
```tsx
|
|
68
|
+
const [mounted, setMounted] = useState(false);
|
|
69
|
+
useEffect(() => setMounted(true), []);
|
|
70
|
+
if (!mounted) return null; // or skeleton
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
# Warnings - important but not critical
|
|
74
|
+
- code: REACT_HELMET_NOT_ASYNC
|
|
75
|
+
severity: warning
|
|
76
|
+
title: "Using react-helmet instead of react-helmet-async"
|
|
77
|
+
description: "react-helmet has memory leaks and doesn't work properly with SSR."
|
|
78
|
+
howToFix: |
|
|
79
|
+
Replace react-helmet with react-helmet-async:
|
|
80
|
+
```bash
|
|
81
|
+
npm uninstall react-helmet
|
|
82
|
+
npm install react-helmet-async
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Update imports:
|
|
86
|
+
```tsx
|
|
87
|
+
// Before
|
|
88
|
+
import { Helmet } from 'react-helmet';
|
|
89
|
+
|
|
90
|
+
// After
|
|
91
|
+
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
|
92
|
+
|
|
93
|
+
// Wrap app in provider
|
|
94
|
+
<HelmetProvider>
|
|
95
|
+
<App />
|
|
96
|
+
</HelmetProvider>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- code: REACT_ROUTER_NO_SSR
|
|
100
|
+
severity: warning
|
|
101
|
+
title: "React Router without server-side support"
|
|
102
|
+
description: "Client-side routing breaks when crawlers access URLs directly."
|
|
103
|
+
howToFix: |
|
|
104
|
+
Ensure your server returns the same HTML for all routes.
|
|
105
|
+
|
|
106
|
+
For static hosting (Netlify, Vercel):
|
|
107
|
+
Add a redirect rule to serve index.html for all routes.
|
|
108
|
+
|
|
109
|
+
netlify.toml:
|
|
110
|
+
```toml
|
|
111
|
+
[[redirects]]
|
|
112
|
+
from = "/*"
|
|
113
|
+
to = "/index.html"
|
|
114
|
+
status = 200
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
- code: REACT_NO_PRERENDER
|
|
118
|
+
severity: warning
|
|
119
|
+
title: "No prerendering configured"
|
|
120
|
+
description: "Static pages could be prerendered for better SEO and performance."
|
|
121
|
+
howToFix: |
|
|
122
|
+
Add react-snap for build-time prerendering:
|
|
123
|
+
```bash
|
|
124
|
+
npm install react-snap
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
package.json:
|
|
128
|
+
```json
|
|
129
|
+
{
|
|
130
|
+
"scripts": {
|
|
131
|
+
"postbuild": "react-snap"
|
|
132
|
+
},
|
|
133
|
+
"reactSnap": {
|
|
134
|
+
"source": "build"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
# Performance-related
|
|
140
|
+
- code: REACT_BUNDLE_TOO_LARGE
|
|
141
|
+
severity: warning
|
|
142
|
+
title: "React bundle too large"
|
|
143
|
+
description: "Large JavaScript bundles delay interactivity and hurt Core Web Vitals."
|
|
144
|
+
howToFix: |
|
|
145
|
+
1. Use React.lazy() for code splitting:
|
|
146
|
+
```tsx
|
|
147
|
+
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
|
|
148
|
+
|
|
149
|
+
<Suspense fallback={<Loading />}>
|
|
150
|
+
<HeavyComponent />
|
|
151
|
+
</Suspense>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
2. Analyze bundle with source-map-explorer:
|
|
155
|
+
```bash
|
|
156
|
+
npm install source-map-explorer
|
|
157
|
+
npx source-map-explorer build/static/js/*.js
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
- code: REACT_NO_SUSPENSE
|
|
161
|
+
severity: notice
|
|
162
|
+
title: "Not using Suspense for data fetching"
|
|
163
|
+
description: "Without Suspense, content pops in causing layout shifts (CLS)."
|
|
164
|
+
howToFix: |
|
|
165
|
+
Use Suspense with a data fetching library:
|
|
166
|
+
```tsx
|
|
167
|
+
<Suspense fallback={<Skeleton />}>
|
|
168
|
+
<DataComponent />
|
|
169
|
+
</Suspense>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
performance_tips:
|
|
173
|
+
- "Use React.memo() for expensive components"
|
|
174
|
+
- "Lazy load routes with React.lazy()"
|
|
175
|
+
- "Use useMemo/useCallback to prevent unnecessary rerenders"
|
|
176
|
+
- "Consider using Preact for smaller bundle size"
|
|
177
|
+
- "Use production builds (NODE_ENV=production)"
|
|
178
|
+
|
|
179
|
+
migration_paths:
|
|
180
|
+
nextjs:
|
|
181
|
+
effort: medium
|
|
182
|
+
benefits:
|
|
183
|
+
- "Automatic SSR/SSG"
|
|
184
|
+
- "Built-in Image optimization"
|
|
185
|
+
- "File-based routing"
|
|
186
|
+
- "API routes"
|
|
187
|
+
guide: "https://nextjs.org/docs/migrating/from-create-react-app"
|
|
188
|
+
|
|
189
|
+
remix:
|
|
190
|
+
effort: medium
|
|
191
|
+
benefits:
|
|
192
|
+
- "Nested routing with data loading"
|
|
193
|
+
- "Progressive enhancement"
|
|
194
|
+
- "Better error boundaries"
|
|
195
|
+
guide: "https://remix.run/docs/en/main/guides/migrating-react-router-app"
|
|
196
|
+
|
|
197
|
+
vite-ssr:
|
|
198
|
+
effort: low
|
|
199
|
+
benefits:
|
|
200
|
+
- "Keep existing code"
|
|
201
|
+
- "Add SSR incrementally"
|
|
202
|
+
guide: "https://vite-plugin-ssr.com/"
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# SvelteKit SEO Recipe
|
|
2
|
+
# SvelteKit has excellent SSR and SSG support with minimal overhead
|
|
3
|
+
|
|
4
|
+
framework: sveltekit
|
|
5
|
+
display_name: SvelteKit
|
|
6
|
+
category: meta-framework
|
|
7
|
+
base_framework: svelte
|
|
8
|
+
ssr_default: true
|
|
9
|
+
|
|
10
|
+
seo_strengths:
|
|
11
|
+
- "SSR by default"
|
|
12
|
+
- "Excellent performance (small runtime)"
|
|
13
|
+
- "Built-in prerendering"
|
|
14
|
+
- "File-based routing"
|
|
15
|
+
- "Form actions for progressive enhancement"
|
|
16
|
+
|
|
17
|
+
checks:
|
|
18
|
+
- code: SVELTEKIT_NO_LOAD_FUNCTION
|
|
19
|
+
severity: warning
|
|
20
|
+
title: "Page without load function"
|
|
21
|
+
description: "Data fetching in +page.svelte bypasses SSR. Use +page.server.ts or +page.ts."
|
|
22
|
+
howToFix: |
|
|
23
|
+
Move data fetching to a load function:
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
// src/routes/products/+page.server.ts
|
|
27
|
+
export async function load({ fetch }) {
|
|
28
|
+
const products = await fetch('/api/products').then(r => r.json());
|
|
29
|
+
return { products };
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
```svelte
|
|
34
|
+
<!-- src/routes/products/+page.svelte -->
|
|
35
|
+
<script>
|
|
36
|
+
export let data;
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
{#each data.products as product}
|
|
40
|
+
<div>{product.name}</div>
|
|
41
|
+
{/each}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
- code: SVELTEKIT_NO_LAYOUT_HEAD
|
|
45
|
+
severity: warning
|
|
46
|
+
title: "Missing svelte:head in layout"
|
|
47
|
+
description: "Common meta tags should be in a layout for consistency."
|
|
48
|
+
howToFix: |
|
|
49
|
+
Add svelte:head to your layout:
|
|
50
|
+
|
|
51
|
+
```svelte
|
|
52
|
+
<!-- src/routes/+layout.svelte -->
|
|
53
|
+
<script>
|
|
54
|
+
import { page } from '$app/stores';
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<svelte:head>
|
|
58
|
+
<title>{$page.data.title ?? 'Your Site'}</title>
|
|
59
|
+
<meta name="description" content={$page.data.description ?? 'Default description'} />
|
|
60
|
+
<link rel="canonical" href={$page.url.href} />
|
|
61
|
+
</svelte:head>
|
|
62
|
+
|
|
63
|
+
<slot />
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- code: SVELTEKIT_NO_PRERENDER
|
|
67
|
+
severity: notice
|
|
68
|
+
title: "Static pages not prerendered"
|
|
69
|
+
description: "Static pages should be prerendered for best performance."
|
|
70
|
+
howToFix: |
|
|
71
|
+
Add prerender to static pages:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
// src/routes/about/+page.ts
|
|
75
|
+
export const prerender = true;
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Or set default in svelte.config.js:
|
|
79
|
+
```js
|
|
80
|
+
kit: {
|
|
81
|
+
prerender: {
|
|
82
|
+
default: true
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- code: SVELTEKIT_NO_TRAILING_SLASH
|
|
88
|
+
severity: notice
|
|
89
|
+
title: "Inconsistent trailing slash handling"
|
|
90
|
+
description: "Configure trailing slash to avoid duplicate content."
|
|
91
|
+
howToFix: |
|
|
92
|
+
Set in svelte.config.js:
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
kit: {
|
|
96
|
+
trailingSlash: 'never' // or 'always'
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- code: SVELTEKIT_ENHANCED_IMAGE
|
|
101
|
+
severity: notice
|
|
102
|
+
title: "Not using enhanced images"
|
|
103
|
+
description: "SvelteKit supports enhanced images with @sveltejs/enhanced-img."
|
|
104
|
+
howToFix: |
|
|
105
|
+
Install enhanced images:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
npm install @sveltejs/enhanced-img
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```svelte
|
|
112
|
+
<script>
|
|
113
|
+
import { Image } from '@sveltejs/enhanced-img';
|
|
114
|
+
import heroImage from '$lib/images/hero.jpg';
|
|
115
|
+
</script>
|
|
116
|
+
|
|
117
|
+
<Image src={heroImage} alt="Hero" />
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
- code: SVELTEKIT_NO_SITEMAP
|
|
121
|
+
severity: warning
|
|
122
|
+
title: "Missing sitemap"
|
|
123
|
+
description: "Use svelte-sitemap or generate manually."
|
|
124
|
+
howToFix: |
|
|
125
|
+
Option 1: Use svelte-sitemap
|
|
126
|
+
```bash
|
|
127
|
+
npm install svelte-sitemap
|
|
128
|
+
npx svelte-sitemap --domain https://yoursite.com
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Option 2: Create src/routes/sitemap.xml/+server.ts:
|
|
132
|
+
```ts
|
|
133
|
+
export async function GET() {
|
|
134
|
+
const pages = ['/', '/about', '/products'];
|
|
135
|
+
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
|
|
136
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
137
|
+
${pages.map(page => `
|
|
138
|
+
<url>
|
|
139
|
+
<loc>https://yoursite.com${page}</loc>
|
|
140
|
+
</url>
|
|
141
|
+
`).join('')}
|
|
142
|
+
</urlset>`;
|
|
143
|
+
return new Response(sitemap, {
|
|
144
|
+
headers: { 'Content-Type': 'application/xml' }
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
performance_tips:
|
|
150
|
+
- "Use load functions for SSR data"
|
|
151
|
+
- "Prerender static pages"
|
|
152
|
+
- "Use streaming for slow data sources"
|
|
153
|
+
- "Leverage Svelte's small runtime"
|
|
154
|
+
- "Use form actions for progressive enhancement"
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Vue.js SEO Recipe
|
|
2
|
+
# Similar challenges to React for client-side rendered apps
|
|
3
|
+
|
|
4
|
+
framework: vue
|
|
5
|
+
display_name: Vue.js
|
|
6
|
+
category: spa
|
|
7
|
+
ssr_default: false
|
|
8
|
+
|
|
9
|
+
seo_challenges:
|
|
10
|
+
- "Client-side rendering sends empty HTML to crawlers"
|
|
11
|
+
- "Vue Router requires proper server configuration"
|
|
12
|
+
- "Async components delay content rendering"
|
|
13
|
+
- "No built-in meta tag management"
|
|
14
|
+
|
|
15
|
+
checks:
|
|
16
|
+
- code: VUE_CSR_DETECTED
|
|
17
|
+
severity: error
|
|
18
|
+
title: "Client-side rendered Vue app detected"
|
|
19
|
+
description: "Vue apps without SSR send empty HTML to crawlers."
|
|
20
|
+
impact: "Your content may not be indexed by search engines"
|
|
21
|
+
howToFix: |
|
|
22
|
+
Option 1: Migrate to Nuxt for automatic SSR/SSG
|
|
23
|
+
Option 2: Add SSR with @vue/server-renderer
|
|
24
|
+
Option 3: Use prerendering with prerender-spa-plugin
|
|
25
|
+
|
|
26
|
+
For Nuxt migration, most Vue components work directly.
|
|
27
|
+
|
|
28
|
+
- code: VUE_NO_META_MANAGEMENT
|
|
29
|
+
severity: error
|
|
30
|
+
title: "No meta tag management library"
|
|
31
|
+
description: "Title and meta description are not dynamically managed."
|
|
32
|
+
howToFix: |
|
|
33
|
+
Install @vueuse/head or vue-meta:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @vueuse/head
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// main.ts
|
|
41
|
+
import { createHead } from '@vueuse/head';
|
|
42
|
+
const head = createHead();
|
|
43
|
+
app.use(head);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<script setup>
|
|
48
|
+
import { useHead } from '@vueuse/head';
|
|
49
|
+
|
|
50
|
+
useHead({
|
|
51
|
+
title: 'Page Title',
|
|
52
|
+
meta: [
|
|
53
|
+
{ name: 'description', content: 'Page description' },
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
</script>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
- code: VUE_ROUTER_HISTORY_MODE
|
|
60
|
+
severity: warning
|
|
61
|
+
title: "Vue Router not in history mode"
|
|
62
|
+
description: "Hash mode (#/) creates ugly URLs and SEO issues."
|
|
63
|
+
howToFix: |
|
|
64
|
+
Use createWebHistory instead of createWebHashHistory:
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { createRouter, createWebHistory } from 'vue-router';
|
|
68
|
+
|
|
69
|
+
const router = createRouter({
|
|
70
|
+
history: createWebHistory(),
|
|
71
|
+
routes: [...],
|
|
72
|
+
});
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then configure your server to return index.html for all routes.
|
|
76
|
+
|
|
77
|
+
- code: VUE_ASYNC_COMPONENT_NO_LOADING
|
|
78
|
+
severity: warning
|
|
79
|
+
title: "Async components without loading state"
|
|
80
|
+
description: "Async components can cause CLS when they pop in."
|
|
81
|
+
howToFix: |
|
|
82
|
+
Provide a loading component:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { defineAsyncComponent } from 'vue';
|
|
86
|
+
|
|
87
|
+
const AsyncComp = defineAsyncComponent({
|
|
88
|
+
loader: () => import('./HeavyComponent.vue'),
|
|
89
|
+
loadingComponent: LoadingSpinner,
|
|
90
|
+
delay: 200,
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
- code: VUE_NO_KEY_ON_LIST
|
|
95
|
+
severity: notice
|
|
96
|
+
title: "v-for without :key attribute"
|
|
97
|
+
description: "Missing keys can cause rendering issues and affect SEO crawling."
|
|
98
|
+
howToFix: |
|
|
99
|
+
Always use :key with v-for:
|
|
100
|
+
|
|
101
|
+
```vue
|
|
102
|
+
<div v-for="item in items" :key="item.id">
|
|
103
|
+
{{ item.name }}
|
|
104
|
+
</div>
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- code: VUE_TRANSITION_CLS
|
|
108
|
+
severity: notice
|
|
109
|
+
title: "Transitions may cause layout shifts"
|
|
110
|
+
description: "CSS transitions that change element size cause CLS."
|
|
111
|
+
howToFix: |
|
|
112
|
+
Use transform instead of width/height changes:
|
|
113
|
+
|
|
114
|
+
```css
|
|
115
|
+
/* Bad - causes CLS */
|
|
116
|
+
.fade-enter-active { height: 0 to 100px; }
|
|
117
|
+
|
|
118
|
+
/* Good - no CLS */
|
|
119
|
+
.fade-enter-active { transform: scaleY(0) to scaleY(1); }
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
performance_tips:
|
|
123
|
+
- "Use Vite for faster development and optimized builds"
|
|
124
|
+
- "Lazy load routes with dynamic imports"
|
|
125
|
+
- "Use v-once for static content"
|
|
126
|
+
- "Use v-memo for expensive list items"
|
|
127
|
+
- "Consider Nuxt for SSR/SSG"
|
|
128
|
+
|
|
129
|
+
migration_paths:
|
|
130
|
+
nuxt:
|
|
131
|
+
effort: low
|
|
132
|
+
benefits:
|
|
133
|
+
- "Automatic SSR/SSG"
|
|
134
|
+
- "File-based routing"
|
|
135
|
+
- "Auto-imports"
|
|
136
|
+
- "Built-in SEO utilities"
|
|
137
|
+
guide: "https://nuxt.com/docs/migration/overview"
|
|
@@ -0,0 +1,209 @@
|
|
|
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"
|