@rankcli/agent-runtime 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -944,6 +944,9 @@ __export(index_exports, {
944
944
  frameworks: () => frameworks_exports,
945
945
  generateAICitableContent: () => generateAICitableContent,
946
946
  generateAllFixes: () => generateAllFixes,
947
+ generateAngularSEOService: () => generateAngularSEOService,
948
+ generateAstroBaseHead: () => generateAstroBaseHead,
949
+ generateAstroLayout: () => generateAstroLayout,
947
950
  generateAstroMeta: () => generateAstroMeta,
948
951
  generateBlogPost: () => generateBlogPost,
949
952
  generateBranchName: () => generateBranchName,
@@ -968,22 +971,35 @@ __export(index_exports, {
968
971
  generateKeyFacts: () => generateKeyFacts,
969
972
  generateMarkdownReport: () => generateMarkdownReport,
970
973
  generateNextAppMetadata: () => generateNextAppMetadata,
974
+ generateNextJsAppRouterMetadata: () => generateNextJsAppRouterMetadata,
975
+ generateNextJsDynamicMetadata: () => generateNextJsDynamicMetadata,
976
+ generateNextJsPageMetadata: () => generateNextJsPageMetadata,
977
+ generateNextJsPagesRouterHead: () => generateNextJsPagesRouterHead,
978
+ generateNextJsRobots: () => generateNextJsRobots,
979
+ generateNextJsSitemap: () => generateNextJsSitemap,
971
980
  generateNextPagesHead: () => generateNextPagesHead,
981
+ generateNuxtPageExample: () => generateNuxtPageExample,
982
+ generateNuxtSEOHead: () => generateNuxtSEOHead,
972
983
  generatePDFReport: () => generatePDFReport,
973
984
  generatePRDescription: () => generatePRDescription,
985
+ generateReactAppWrapper: () => generateReactAppWrapper,
974
986
  generateReactHelmetSocialMeta: () => generateReactHelmetSocialMeta,
987
+ generateReactSEOHead: () => generateReactSEOHead,
975
988
  generateRecommendationQueries: () => generateRecommendationQueries,
976
989
  generateRemixMeta: () => generateRemixMeta,
977
990
  generateSecretsDoc: () => generateSecretsDoc,
978
991
  generateSocialMetaFix: () => generateSocialMetaFix,
979
992
  generateSvelteKitMeta: () => generateSvelteKitMeta,
993
+ generateSvelteKitSEOHead: () => generateSvelteKitSEOHead,
980
994
  generateUncertaintyQuestions: () => generateUncertaintyQuestions,
995
+ generateVueSEOHead: () => generateVueSEOHead,
981
996
  generateWizardQuestions: () => generateWizardQuestions,
982
997
  generateWorkflow: () => generateWorkflow,
983
998
  getAIVisibilitySummary: () => getAIVisibilitySummary,
984
999
  getAutocompleteSuggestions: () => getAutocompleteSuggestions,
985
1000
  getDateRange: () => getDateRange,
986
1001
  getExpandedSuggestions: () => getExpandedSuggestions,
1002
+ getFrameworkSpecificFix: () => getFrameworkSpecificFix,
987
1003
  getGSCSetupInstructions: () => getGSCSetupInstructions,
988
1004
  getGitUser: () => getGitUser,
989
1005
  getKeywordData: () => getKeywordData,
@@ -18218,7 +18234,9 @@ var MEDIUM_PRIORITY_PATTERNS = [
18218
18234
  ];
18219
18235
  var EXCLUDE_PATTERNS = [
18220
18236
  /\.(png|jpg|jpeg|gif|svg|webp|ico|pdf|zip|tar|gz)$/i,
18221
- // Files
18237
+ // Images & archives
18238
+ /\.(json|xml|txt|webmanifest|rss|atom)$/i,
18239
+ // Data files (not HTML)
18222
18240
  /^(mailto:|tel:|javascript:|#)/i,
18223
18241
  // Non-HTTP
18224
18242
  /\/(api|_next|static|assets|cdn)\//i,
@@ -26720,6 +26738,658 @@ async function runDirectAnalysis(url) {
26720
26738
  // src/fixer.ts
26721
26739
  var import_fs4 = require("fs");
26722
26740
  var import_path4 = require("path");
26741
+
26742
+ // src/fixer/framework-fixes.ts
26743
+ function generateReactSEOHead(options) {
26744
+ const { siteName, siteUrl, title, description, image } = options;
26745
+ return {
26746
+ file: "src/components/SEOHead.tsx",
26747
+ code: `import { Helmet } from 'react-helmet-async';
26748
+
26749
+ interface SEOHeadProps {
26750
+ title?: string;
26751
+ description?: string;
26752
+ image?: string;
26753
+ url?: string;
26754
+ type?: 'website' | 'article';
26755
+ }
26756
+
26757
+ export function SEOHead({
26758
+ title = '${title || `${siteName} - Your tagline here`}',
26759
+ description = '${description || `${siteName} - A compelling description of your product or service.`}',
26760
+ image = '${image || `${siteUrl}/og-image.png`}',
26761
+ url = typeof window !== 'undefined' ? window.location.href : '${siteUrl}',
26762
+ type = 'website',
26763
+ }: SEOHeadProps) {
26764
+ const fullTitle = title.includes('${siteName}') ? title : \`\${title} | ${siteName}\`;
26765
+
26766
+ return (
26767
+ <Helmet>
26768
+ {/* Primary Meta Tags */}
26769
+ <title>{fullTitle}</title>
26770
+ <meta name="description" content={description} />
26771
+ <link rel="canonical" href={url} />
26772
+
26773
+ {/* Open Graph / Facebook */}
26774
+ <meta property="og:type" content={type} />
26775
+ <meta property="og:url" content={url} />
26776
+ <meta property="og:title" content={fullTitle} />
26777
+ <meta property="og:description" content={description} />
26778
+ <meta property="og:image" content={image} />
26779
+ <meta property="og:site_name" content="${siteName}" />
26780
+
26781
+ {/* Twitter */}
26782
+ <meta name="twitter:card" content="summary_large_image" />
26783
+ <meta name="twitter:url" content={url} />
26784
+ <meta name="twitter:title" content={fullTitle} />
26785
+ <meta name="twitter:description" content={description} />
26786
+ <meta name="twitter:image" content={image} />
26787
+ </Helmet>
26788
+ );
26789
+ }`,
26790
+ explanation: "React SEO component using react-helmet-async. Wrap your app in <HelmetProvider> and use <SEOHead /> on each page.",
26791
+ installCommands: ["npm install react-helmet-async"],
26792
+ imports: ["import { HelmetProvider } from 'react-helmet-async';"]
26793
+ };
26794
+ }
26795
+ function generateReactAppWrapper() {
26796
+ return {
26797
+ file: "src/main.tsx",
26798
+ code: `import React from 'react';
26799
+ import ReactDOM from 'react-dom/client';
26800
+ import { HelmetProvider } from 'react-helmet-async';
26801
+ import App from './App';
26802
+ import './index.css';
26803
+
26804
+ ReactDOM.createRoot(document.getElementById('root')!).render(
26805
+ <React.StrictMode>
26806
+ <HelmetProvider>
26807
+ <App />
26808
+ </HelmetProvider>
26809
+ </React.StrictMode>,
26810
+ );`,
26811
+ explanation: "Updated main.tsx with HelmetProvider wrapper for react-helmet-async."
26812
+ };
26813
+ }
26814
+ function generateNextJsAppRouterMetadata(options) {
26815
+ const { siteName, siteUrl, title, description, image } = options;
26816
+ return {
26817
+ file: "app/layout.tsx",
26818
+ code: `import type { Metadata } from 'next';
26819
+ import { Inter } from 'next/font/google';
26820
+ import './globals.css';
26821
+
26822
+ const inter = Inter({ subsets: ['latin'] });
26823
+
26824
+ export const metadata: Metadata = {
26825
+ metadataBase: new URL('${siteUrl}'),
26826
+ title: {
26827
+ default: '${title || siteName}',
26828
+ template: \`%s | ${siteName}\`,
26829
+ },
26830
+ description: '${description || `${siteName} - A compelling description of your product or service.`}',
26831
+ openGraph: {
26832
+ type: 'website',
26833
+ locale: 'en_US',
26834
+ url: '${siteUrl}',
26835
+ siteName: '${siteName}',
26836
+ title: '${title || siteName}',
26837
+ description: '${description || `${siteName} - A compelling description.`}',
26838
+ images: [
26839
+ {
26840
+ url: '${image || "/og-image.png"}',
26841
+ width: 1200,
26842
+ height: 630,
26843
+ alt: '${siteName}',
26844
+ },
26845
+ ],
26846
+ },
26847
+ twitter: {
26848
+ card: 'summary_large_image',
26849
+ title: '${title || siteName}',
26850
+ description: '${description || `${siteName} - A compelling description.`}',
26851
+ images: ['${image || "/og-image.png"}'],
26852
+ },
26853
+ robots: {
26854
+ index: true,
26855
+ follow: true,
26856
+ },
26857
+ };
26858
+
26859
+ export default function RootLayout({
26860
+ children,
26861
+ }: {
26862
+ children: React.ReactNode;
26863
+ }) {
26864
+ return (
26865
+ <html lang="en">
26866
+ <body className={inter.className}>{children}</body>
26867
+ </html>
26868
+ );
26869
+ }`,
26870
+ explanation: "Next.js App Router layout with built-in Metadata API. Each page can override with its own metadata export."
26871
+ };
26872
+ }
26873
+ function generateNextJsPageMetadata(options) {
26874
+ const { siteName, description, pageName } = options;
26875
+ return {
26876
+ file: `app/${pageName}/page.tsx`,
26877
+ code: `import type { Metadata } from 'next';
26878
+
26879
+ export const metadata: Metadata = {
26880
+ title: '${pageName.charAt(0).toUpperCase() + pageName.slice(1)}',
26881
+ description: '${description || `Learn more about ${pageName} on ${siteName}.`}',
26882
+ };
26883
+
26884
+ export default function ${pageName.charAt(0).toUpperCase() + pageName.slice(1)}Page() {
26885
+ return (
26886
+ <main>
26887
+ <h1>${pageName.charAt(0).toUpperCase() + pageName.slice(1)}</h1>
26888
+ {/* Your content here */}
26889
+ </main>
26890
+ );
26891
+ }`,
26892
+ explanation: `Next.js page with metadata export. The title will be "${pageName} | ${siteName}" using the template.`
26893
+ };
26894
+ }
26895
+ function generateNextJsDynamicMetadata() {
26896
+ return {
26897
+ file: "app/[slug]/page.tsx",
26898
+ code: `import type { Metadata } from 'next';
26899
+
26900
+ interface PageProps {
26901
+ params: { slug: string };
26902
+ }
26903
+
26904
+ // Generate metadata dynamically based on the slug
26905
+ export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
26906
+ // Fetch data based on slug (replace with your data fetching logic)
26907
+ const data = await fetchPageData(params.slug);
26908
+
26909
+ return {
26910
+ title: data.title,
26911
+ description: data.description,
26912
+ openGraph: {
26913
+ title: data.title,
26914
+ description: data.description,
26915
+ images: data.image ? [{ url: data.image }] : [],
26916
+ },
26917
+ };
26918
+ }
26919
+
26920
+ // Pre-generate static pages for known slugs (improves SEO)
26921
+ export async function generateStaticParams() {
26922
+ const pages = await fetchAllPageSlugs();
26923
+ return pages.map((slug) => ({ slug }));
26924
+ }
26925
+
26926
+ async function fetchPageData(slug: string) {
26927
+ // Replace with your actual data fetching
26928
+ return {
26929
+ title: slug.replace(/-/g, ' ').replace(/\\b\\w/g, c => c.toUpperCase()),
26930
+ description: \`Learn about \${slug}\`,
26931
+ image: null,
26932
+ };
26933
+ }
26934
+
26935
+ async function fetchAllPageSlugs() {
26936
+ // Replace with your actual data source
26937
+ return ['about', 'contact', 'pricing'];
26938
+ }
26939
+
26940
+ export default function DynamicPage({ params }: PageProps) {
26941
+ return (
26942
+ <main>
26943
+ <h1>{params.slug}</h1>
26944
+ </main>
26945
+ );
26946
+ }`,
26947
+ explanation: "Next.js dynamic route with generateMetadata and generateStaticParams for SEO-optimized dynamic pages."
26948
+ };
26949
+ }
26950
+ function generateNextJsRobots(siteUrl) {
26951
+ return {
26952
+ file: "app/robots.ts",
26953
+ code: `import type { MetadataRoute } from 'next';
26954
+
26955
+ export default function robots(): MetadataRoute.Robots {
26956
+ return {
26957
+ rules: [
26958
+ {
26959
+ userAgent: '*',
26960
+ allow: '/',
26961
+ disallow: ['/api/', '/admin/', '/_next/'],
26962
+ },
26963
+ ],
26964
+ sitemap: '${siteUrl}/sitemap.xml',
26965
+ };
26966
+ }`,
26967
+ explanation: "Next.js App Router robots.txt generator. Automatically served at /robots.txt."
26968
+ };
26969
+ }
26970
+ function generateNextJsSitemap(siteUrl) {
26971
+ return {
26972
+ file: "app/sitemap.ts",
26973
+ code: `import type { MetadataRoute } from 'next';
26974
+
26975
+ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
26976
+ // Static pages
26977
+ const staticPages: MetadataRoute.Sitemap = [
26978
+ {
26979
+ url: '${siteUrl}',
26980
+ lastModified: new Date(),
26981
+ changeFrequency: 'daily',
26982
+ priority: 1,
26983
+ },
26984
+ {
26985
+ url: '${siteUrl}/about',
26986
+ lastModified: new Date(),
26987
+ changeFrequency: 'monthly',
26988
+ priority: 0.8,
26989
+ },
26990
+ {
26991
+ url: '${siteUrl}/pricing',
26992
+ lastModified: new Date(),
26993
+ changeFrequency: 'weekly',
26994
+ priority: 0.9,
26995
+ },
26996
+ ];
26997
+
26998
+ // Dynamic pages (fetch from your database/CMS)
26999
+ // const posts = await fetchAllPosts();
27000
+ // const dynamicPages = posts.map((post) => ({
27001
+ // url: \`${siteUrl}/blog/\${post.slug}\`,
27002
+ // lastModified: post.updatedAt,
27003
+ // changeFrequency: 'weekly' as const,
27004
+ // priority: 0.7,
27005
+ // }));
27006
+
27007
+ return [...staticPages];
27008
+ }`,
27009
+ explanation: "Next.js App Router sitemap generator. Automatically served at /sitemap.xml."
27010
+ };
27011
+ }
27012
+ function generateNextJsPagesRouterHead(options) {
27013
+ const { siteName, siteUrl, title, description, image } = options;
27014
+ return {
27015
+ file: "components/SEOHead.tsx",
27016
+ code: `import Head from 'next/head';
27017
+
27018
+ interface SEOHeadProps {
27019
+ title?: string;
27020
+ description?: string;
27021
+ image?: string;
27022
+ url?: string;
27023
+ type?: 'website' | 'article';
27024
+ }
27025
+
27026
+ export function SEOHead({
27027
+ title = '${title || siteName}',
27028
+ description = '${description || `${siteName} - A compelling description.`}',
27029
+ image = '${image || `${siteUrl}/og-image.png`}',
27030
+ url = '${siteUrl}',
27031
+ type = 'website',
27032
+ }: SEOHeadProps) {
27033
+ const fullTitle = title.includes('${siteName}') ? title : \`\${title} | ${siteName}\`;
27034
+
27035
+ return (
27036
+ <Head>
27037
+ <title>{fullTitle}</title>
27038
+ <meta name="description" content={description} />
27039
+ <link rel="canonical" href={url} />
27040
+
27041
+ <meta property="og:type" content={type} />
27042
+ <meta property="og:url" content={url} />
27043
+ <meta property="og:title" content={fullTitle} />
27044
+ <meta property="og:description" content={description} />
27045
+ <meta property="og:image" content={image} />
27046
+ <meta property="og:site_name" content="${siteName}" />
27047
+
27048
+ <meta name="twitter:card" content="summary_large_image" />
27049
+ <meta name="twitter:title" content={fullTitle} />
27050
+ <meta name="twitter:description" content={description} />
27051
+ <meta name="twitter:image" content={image} />
27052
+ </Head>
27053
+ );
27054
+ }`,
27055
+ explanation: "Next.js Pages Router SEO component using next/head. Import and use on each page."
27056
+ };
27057
+ }
27058
+ function generateNuxtSEOHead(options) {
27059
+ const { siteName, siteUrl, title, description, image } = options;
27060
+ return {
27061
+ file: "composables/useSEO.ts",
27062
+ code: `export function useSEO(options: {
27063
+ title?: string;
27064
+ description?: string;
27065
+ image?: string;
27066
+ url?: string;
27067
+ type?: 'website' | 'article';
27068
+ } = {}) {
27069
+ const route = useRoute();
27070
+ const config = useRuntimeConfig();
27071
+
27072
+ const defaults = {
27073
+ title: '${title || siteName}',
27074
+ description: '${description || `${siteName} - A compelling description.`}',
27075
+ image: '${image || `${siteUrl}/og-image.png`}',
27076
+ url: \`${siteUrl}\${route.path}\`,
27077
+ type: 'website' as const,
27078
+ };
27079
+
27080
+ const meta = { ...defaults, ...options };
27081
+ const fullTitle = meta.title.includes('${siteName}')
27082
+ ? meta.title
27083
+ : \`\${meta.title} | ${siteName}\`;
27084
+
27085
+ useHead({
27086
+ title: fullTitle,
27087
+ meta: [
27088
+ { name: 'description', content: meta.description },
27089
+ // Open Graph
27090
+ { property: 'og:type', content: meta.type },
27091
+ { property: 'og:url', content: meta.url },
27092
+ { property: 'og:title', content: fullTitle },
27093
+ { property: 'og:description', content: meta.description },
27094
+ { property: 'og:image', content: meta.image },
27095
+ { property: 'og:site_name', content: '${siteName}' },
27096
+ // Twitter
27097
+ { name: 'twitter:card', content: 'summary_large_image' },
27098
+ { name: 'twitter:title', content: fullTitle },
27099
+ { name: 'twitter:description', content: meta.description },
27100
+ { name: 'twitter:image', content: meta.image },
27101
+ ],
27102
+ link: [
27103
+ { rel: 'canonical', href: meta.url },
27104
+ ],
27105
+ });
27106
+ }`,
27107
+ explanation: "Nuxt 3 SEO composable using useHead(). Call useSEO() in any page to set meta tags."
27108
+ };
27109
+ }
27110
+ function generateNuxtPageExample() {
27111
+ return {
27112
+ file: "pages/about.vue",
27113
+ code: `<script setup lang="ts">
27114
+ useSEO({
27115
+ title: 'About Us',
27116
+ description: 'Learn more about our company and mission.',
27117
+ });
27118
+ </script>
27119
+
27120
+ <template>
27121
+ <main>
27122
+ <h1>About Us</h1>
27123
+ <!-- Your content here -->
27124
+ </main>
27125
+ </template>`,
27126
+ explanation: "Example Nuxt page using the useSEO composable."
27127
+ };
27128
+ }
27129
+ function generateVueSEOHead(options) {
27130
+ const { siteName, siteUrl, title, description, image } = options;
27131
+ return {
27132
+ file: "src/composables/useSEO.ts",
27133
+ code: `import { useHead } from '@unhead/vue';
27134
+ import { computed, ref } from 'vue';
27135
+
27136
+ interface SEOOptions {
27137
+ title?: string;
27138
+ description?: string;
27139
+ image?: string;
27140
+ url?: string;
27141
+ type?: 'website' | 'article';
27142
+ }
27143
+
27144
+ export function useSEO(options: SEOOptions = {}) {
27145
+ const defaults = {
27146
+ title: '${title || siteName}',
27147
+ description: '${description || `${siteName} - A compelling description.`}',
27148
+ image: '${image || `${siteUrl}/og-image.png`}',
27149
+ url: typeof window !== 'undefined' ? window.location.href : '${siteUrl}',
27150
+ type: 'website' as const,
27151
+ };
27152
+
27153
+ const meta = { ...defaults, ...options };
27154
+ const fullTitle = computed(() =>
27155
+ meta.title.includes('${siteName}') ? meta.title : \`\${meta.title} | ${siteName}\`
27156
+ );
27157
+
27158
+ useHead({
27159
+ title: fullTitle,
27160
+ meta: [
27161
+ { name: 'description', content: meta.description },
27162
+ { property: 'og:type', content: meta.type },
27163
+ { property: 'og:url', content: meta.url },
27164
+ { property: 'og:title', content: fullTitle.value },
27165
+ { property: 'og:description', content: meta.description },
27166
+ { property: 'og:image', content: meta.image },
27167
+ { property: 'og:site_name', content: '${siteName}' },
27168
+ { name: 'twitter:card', content: 'summary_large_image' },
27169
+ { name: 'twitter:title', content: fullTitle.value },
27170
+ { name: 'twitter:description', content: meta.description },
27171
+ { name: 'twitter:image', content: meta.image },
27172
+ ],
27173
+ link: [
27174
+ { rel: 'canonical', href: meta.url },
27175
+ ],
27176
+ });
27177
+ }`,
27178
+ explanation: "Vue 3 SEO composable using @unhead/vue. Install with: npm install @unhead/vue",
27179
+ installCommands: ["npm install @unhead/vue"]
27180
+ };
27181
+ }
27182
+ function generateAstroBaseHead(options) {
27183
+ const { siteName, siteUrl, title, description, image } = options;
27184
+ return {
27185
+ file: "src/components/BaseHead.astro",
27186
+ code: `---
27187
+ interface Props {
27188
+ title?: string;
27189
+ description?: string;
27190
+ image?: string;
27191
+ type?: 'website' | 'article';
27192
+ }
27193
+
27194
+ const {
27195
+ title = '${title || siteName}',
27196
+ description = '${description || `${siteName} - A compelling description.`}',
27197
+ image = '${image || "/og-image.png"}',
27198
+ type = 'website',
27199
+ } = Astro.props;
27200
+
27201
+ const canonicalURL = new URL(Astro.url.pathname, Astro.site);
27202
+ const fullTitle = title.includes('${siteName}') ? title : \`\${title} | ${siteName}\`;
27203
+ const imageURL = new URL(image, Astro.site);
27204
+ ---
27205
+
27206
+ <!-- Global Metadata -->
27207
+ <meta charset="utf-8" />
27208
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
27209
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
27210
+ <meta name="generator" content={Astro.generator} />
27211
+
27212
+ <!-- Canonical URL -->
27213
+ <link rel="canonical" href={canonicalURL} />
27214
+
27215
+ <!-- Primary Meta Tags -->
27216
+ <title>{fullTitle}</title>
27217
+ <meta name="title" content={fullTitle} />
27218
+ <meta name="description" content={description} />
27219
+
27220
+ <!-- Open Graph / Facebook -->
27221
+ <meta property="og:type" content={type} />
27222
+ <meta property="og:url" content={canonicalURL} />
27223
+ <meta property="og:title" content={fullTitle} />
27224
+ <meta property="og:description" content={description} />
27225
+ <meta property="og:image" content={imageURL} />
27226
+ <meta property="og:site_name" content="${siteName}" />
27227
+
27228
+ <!-- Twitter -->
27229
+ <meta name="twitter:card" content="summary_large_image" />
27230
+ <meta name="twitter:url" content={canonicalURL} />
27231
+ <meta name="twitter:title" content={fullTitle} />
27232
+ <meta name="twitter:description" content={description} />
27233
+ <meta name="twitter:image" content={imageURL} />`,
27234
+ explanation: 'Astro BaseHead component. Import in your layout: <BaseHead title="Page Title" description="..." />'
27235
+ };
27236
+ }
27237
+ function generateAstroLayout(siteName) {
27238
+ return {
27239
+ file: "src/layouts/BaseLayout.astro",
27240
+ code: `---
27241
+ import BaseHead from '../components/BaseHead.astro';
27242
+
27243
+ interface Props {
27244
+ title?: string;
27245
+ description?: string;
27246
+ image?: string;
27247
+ }
27248
+
27249
+ const { title, description, image } = Astro.props;
27250
+ ---
27251
+
27252
+ <!DOCTYPE html>
27253
+ <html lang="en">
27254
+ <head>
27255
+ <BaseHead title={title} description={description} image={image} />
27256
+ </head>
27257
+ <body>
27258
+ <main>
27259
+ <slot />
27260
+ </main>
27261
+ </body>
27262
+ </html>`,
27263
+ explanation: "Astro layout using BaseHead component. Use in pages with frontmatter."
27264
+ };
27265
+ }
27266
+ function generateSvelteKitSEOHead(options) {
27267
+ const { siteName, siteUrl, title, description, image } = options;
27268
+ return {
27269
+ file: "src/lib/components/SEOHead.svelte",
27270
+ code: `<script lang="ts">
27271
+ import { page } from '$app/stores';
27272
+
27273
+ export let title = '${title || siteName}';
27274
+ export let description = '${description || `${siteName} - A compelling description.`}';
27275
+ export let image = '${image || `${siteUrl}/og-image.png`}';
27276
+ export let type: 'website' | 'article' = 'website';
27277
+
27278
+ $: fullTitle = title.includes('${siteName}') ? title : \`\${title} | ${siteName}\`;
27279
+ $: canonicalUrl = $page.url.href;
27280
+ </script>
27281
+
27282
+ <svelte:head>
27283
+ <title>{fullTitle}</title>
27284
+ <meta name="description" content={description} />
27285
+ <link rel="canonical" href={canonicalUrl} />
27286
+
27287
+ <meta property="og:type" content={type} />
27288
+ <meta property="og:url" content={canonicalUrl} />
27289
+ <meta property="og:title" content={fullTitle} />
27290
+ <meta property="og:description" content={description} />
27291
+ <meta property="og:image" content={image} />
27292
+ <meta property="og:site_name" content="${siteName}" />
27293
+
27294
+ <meta name="twitter:card" content="summary_large_image" />
27295
+ <meta name="twitter:title" content={fullTitle} />
27296
+ <meta name="twitter:description" content={description} />
27297
+ <meta name="twitter:image" content={image} />
27298
+ </svelte:head>`,
27299
+ explanation: "SvelteKit SEO component using svelte:head. Import and use in +page.svelte files."
27300
+ };
27301
+ }
27302
+ function generateAngularSEOService(options) {
27303
+ const { siteName, siteUrl, title, description, image } = options;
27304
+ return {
27305
+ file: "src/app/services/seo.service.ts",
27306
+ code: `import { Injectable } from '@angular/core';
27307
+ import { Meta, Title } from '@angular/platform-browser';
27308
+ import { Router } from '@angular/router';
27309
+
27310
+ interface SEOConfig {
27311
+ title?: string;
27312
+ description?: string;
27313
+ image?: string;
27314
+ type?: 'website' | 'article';
27315
+ }
27316
+
27317
+ @Injectable({
27318
+ providedIn: 'root'
27319
+ })
27320
+ export class SEOService {
27321
+ private siteName = '${siteName}';
27322
+ private siteUrl = '${siteUrl}';
27323
+ private defaultDescription = '${description || `${siteName} - A compelling description.`}';
27324
+ private defaultImage = '${image || `${siteUrl}/og-image.png`}';
27325
+
27326
+ constructor(
27327
+ private meta: Meta,
27328
+ private titleService: Title,
27329
+ private router: Router
27330
+ ) {}
27331
+
27332
+ updateMeta(config: SEOConfig = {}): void {
27333
+ const title = config.title || this.siteName;
27334
+ const fullTitle = title.includes(this.siteName) ? title : \`\${title} | \${this.siteName}\`;
27335
+ const description = config.description || this.defaultDescription;
27336
+ const image = config.image || this.defaultImage;
27337
+ const url = this.siteUrl + this.router.url;
27338
+ const type = config.type || 'website';
27339
+
27340
+ // Title
27341
+ this.titleService.setTitle(fullTitle);
27342
+
27343
+ // Primary Meta
27344
+ this.meta.updateTag({ name: 'description', content: description });
27345
+ this.meta.updateTag({ rel: 'canonical', href: url });
27346
+
27347
+ // Open Graph
27348
+ this.meta.updateTag({ property: 'og:type', content: type });
27349
+ this.meta.updateTag({ property: 'og:url', content: url });
27350
+ this.meta.updateTag({ property: 'og:title', content: fullTitle });
27351
+ this.meta.updateTag({ property: 'og:description', content: description });
27352
+ this.meta.updateTag({ property: 'og:image', content: image });
27353
+ this.meta.updateTag({ property: 'og:site_name', content: this.siteName });
27354
+
27355
+ // Twitter
27356
+ this.meta.updateTag({ name: 'twitter:card', content: 'summary_large_image' });
27357
+ this.meta.updateTag({ name: 'twitter:title', content: fullTitle });
27358
+ this.meta.updateTag({ name: 'twitter:description', content: description });
27359
+ this.meta.updateTag({ name: 'twitter:image', content: image });
27360
+ }
27361
+ }`,
27362
+ explanation: "Angular SEO service using Meta and Title services. Inject and call updateMeta() in components."
27363
+ };
27364
+ }
27365
+ function getFrameworkSpecificFix(framework, options) {
27366
+ const name = framework.name.toLowerCase();
27367
+ if (name.includes("next")) {
27368
+ if (framework.router === "app") {
27369
+ return generateNextJsAppRouterMetadata(options);
27370
+ } else {
27371
+ return generateNextJsPagesRouterHead(options);
27372
+ }
27373
+ }
27374
+ if (name.includes("nuxt")) {
27375
+ return generateNuxtSEOHead(options);
27376
+ }
27377
+ if (name.includes("vue")) {
27378
+ return generateVueSEOHead(options);
27379
+ }
27380
+ if (name.includes("astro")) {
27381
+ return generateAstroBaseHead(options);
27382
+ }
27383
+ if (name.includes("svelte")) {
27384
+ return generateSvelteKitSEOHead(options);
27385
+ }
27386
+ if (name.includes("angular")) {
27387
+ return generateAngularSEOService(options);
27388
+ }
27389
+ return generateReactSEOHead(options);
27390
+ }
27391
+
27392
+ // src/fixer.ts
26723
27393
  async function generateFixes(issues, options) {
26724
27394
  const { cwd, url } = options;
26725
27395
  let framework = options.framework;
@@ -27089,60 +27759,26 @@ function generateSitemapFix(context, url) {
27089
27759
  };
27090
27760
  }
27091
27761
  function generateSPAMetaFix(context, framework) {
27092
- const { htmlPath } = context;
27093
- const frameworkLower = framework.name.toLowerCase();
27094
- if (frameworkLower.includes("react") || frameworkLower.includes("vite") || framework.name === "Unknown") {
27095
- return {
27096
- issue: { code: "SPA_NO_META_MANAGEMENT", message: "SPA without dynamic meta tag management", severity: "warning" },
27097
- file: "src/components/SEOHead.tsx",
27098
- before: null,
27099
- after: `import { Helmet } from 'react-helmet-async';
27100
-
27101
- interface SEOHeadProps {
27102
- title?: string;
27103
- description?: string;
27104
- image?: string;
27105
- url?: string;
27106
- }
27762
+ const { url } = context;
27763
+ const fullUrl = url || "https://example.com";
27764
+ const siteName = new URL(fullUrl).hostname.replace("www.", "").split(".")[0];
27765
+ const capitalizedName = siteName.charAt(0).toUpperCase() + siteName.slice(1);
27766
+ const generatedCode = getFrameworkSpecificFix(framework, {
27767
+ siteName: capitalizedName,
27768
+ siteUrl: fullUrl,
27769
+ title: `${capitalizedName} - Your tagline here`,
27770
+ description: `${capitalizedName} - A compelling description of your product or service.`,
27771
+ image: `${fullUrl}/og-image.png`
27772
+ });
27773
+ const installInstructions = generatedCode.installCommands ? `
27107
27774
 
27108
- export function SEOHead({
27109
- title = 'Your Site Name',
27110
- description = 'Your site description',
27111
- image = '/og-image.png',
27112
- url = window.location.href,
27113
- }: SEOHeadProps) {
27114
- return (
27115
- <Helmet>
27116
- <title>{title}</title>
27117
- <meta name="description" content={description} />
27118
- <link rel="canonical" href={url} />
27119
-
27120
- {/* Open Graph */}
27121
- <meta property="og:title" content={title} />
27122
- <meta property="og:description" content={description} />
27123
- <meta property="og:image" content={image} />
27124
- <meta property="og:url" content={url} />
27125
- <meta property="og:type" content="website" />
27126
-
27127
- {/* Twitter */}
27128
- <meta name="twitter:card" content="summary_large_image" />
27129
- <meta name="twitter:title" content={title} />
27130
- <meta name="twitter:description" content={description} />
27131
- <meta name="twitter:image" content={image} />
27132
- </Helmet>
27133
- );
27134
- }`,
27135
- explanation: "Created SEOHead component using react-helmet-async for dynamic meta tags. Install: npm install react-helmet-async"
27136
- };
27137
- }
27775
+ Install: ${generatedCode.installCommands.join(" && ")}` : "";
27138
27776
  return {
27139
- issue: { code: "SPA_NO_META_MANAGEMENT", message: "SPA without meta management", severity: "warning" },
27140
- file: htmlPath,
27777
+ issue: { code: "SPA_NO_META_MANAGEMENT", message: "SPA without dynamic meta tag management", severity: "warning" },
27778
+ file: generatedCode.file,
27141
27779
  before: null,
27142
- after: "<!-- Add a meta management library for your framework -->",
27143
- explanation: `Add dynamic meta tag management for ${framework.name}`,
27144
- skipped: true,
27145
- skipReason: `Framework-specific solution needed for ${framework.name}`
27780
+ after: generatedCode.code,
27781
+ explanation: generatedCode.explanation + installInstructions
27146
27782
  };
27147
27783
  }
27148
27784
  function generatePreconnectFix(context) {
@@ -29986,6 +30622,9 @@ if (typeof globalThis !== "undefined") {
29986
30622
  frameworks,
29987
30623
  generateAICitableContent,
29988
30624
  generateAllFixes,
30625
+ generateAngularSEOService,
30626
+ generateAstroBaseHead,
30627
+ generateAstroLayout,
29989
30628
  generateAstroMeta,
29990
30629
  generateBlogPost,
29991
30630
  generateBranchName,
@@ -30010,22 +30649,35 @@ if (typeof globalThis !== "undefined") {
30010
30649
  generateKeyFacts,
30011
30650
  generateMarkdownReport,
30012
30651
  generateNextAppMetadata,
30652
+ generateNextJsAppRouterMetadata,
30653
+ generateNextJsDynamicMetadata,
30654
+ generateNextJsPageMetadata,
30655
+ generateNextJsPagesRouterHead,
30656
+ generateNextJsRobots,
30657
+ generateNextJsSitemap,
30013
30658
  generateNextPagesHead,
30659
+ generateNuxtPageExample,
30660
+ generateNuxtSEOHead,
30014
30661
  generatePDFReport,
30015
30662
  generatePRDescription,
30663
+ generateReactAppWrapper,
30016
30664
  generateReactHelmetSocialMeta,
30665
+ generateReactSEOHead,
30017
30666
  generateRecommendationQueries,
30018
30667
  generateRemixMeta,
30019
30668
  generateSecretsDoc,
30020
30669
  generateSocialMetaFix,
30021
30670
  generateSvelteKitMeta,
30671
+ generateSvelteKitSEOHead,
30022
30672
  generateUncertaintyQuestions,
30673
+ generateVueSEOHead,
30023
30674
  generateWizardQuestions,
30024
30675
  generateWorkflow,
30025
30676
  getAIVisibilitySummary,
30026
30677
  getAutocompleteSuggestions,
30027
30678
  getDateRange,
30028
30679
  getExpandedSuggestions,
30680
+ getFrameworkSpecificFix,
30029
30681
  getGSCSetupInstructions,
30030
30682
  getGitUser,
30031
30683
  getKeywordData,