@rankcli/agent-runtime 0.0.6 → 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.d.mts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +701 -51
- package/dist/index.mjs +685 -51
- package/package.json +1 -1
- package/src/fixer/framework-fixes.ts +743 -0
- package/src/fixer/index.ts +5 -0
- package/src/fixer.ts +21 -54
- package/src/index.ts +1 -0
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,
|
|
@@ -26722,6 +26738,658 @@ async function runDirectAnalysis(url) {
|
|
|
26722
26738
|
// src/fixer.ts
|
|
26723
26739
|
var import_fs4 = require("fs");
|
|
26724
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
|
|
26725
27393
|
async function generateFixes(issues, options) {
|
|
26726
27394
|
const { cwd, url } = options;
|
|
26727
27395
|
let framework = options.framework;
|
|
@@ -27091,60 +27759,26 @@ function generateSitemapFix(context, url) {
|
|
|
27091
27759
|
};
|
|
27092
27760
|
}
|
|
27093
27761
|
function generateSPAMetaFix(context, framework) {
|
|
27094
|
-
const {
|
|
27095
|
-
const
|
|
27096
|
-
|
|
27097
|
-
|
|
27098
|
-
|
|
27099
|
-
|
|
27100
|
-
|
|
27101
|
-
|
|
27102
|
-
|
|
27103
|
-
|
|
27104
|
-
|
|
27105
|
-
|
|
27106
|
-
image?: string;
|
|
27107
|
-
url?: string;
|
|
27108
|
-
}
|
|
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 ? `
|
|
27109
27774
|
|
|
27110
|
-
|
|
27111
|
-
title = 'Your Site Name',
|
|
27112
|
-
description = 'Your site description',
|
|
27113
|
-
image = '/og-image.png',
|
|
27114
|
-
url = window.location.href,
|
|
27115
|
-
}: SEOHeadProps) {
|
|
27116
|
-
return (
|
|
27117
|
-
<Helmet>
|
|
27118
|
-
<title>{title}</title>
|
|
27119
|
-
<meta name="description" content={description} />
|
|
27120
|
-
<link rel="canonical" href={url} />
|
|
27121
|
-
|
|
27122
|
-
{/* Open Graph */}
|
|
27123
|
-
<meta property="og:title" content={title} />
|
|
27124
|
-
<meta property="og:description" content={description} />
|
|
27125
|
-
<meta property="og:image" content={image} />
|
|
27126
|
-
<meta property="og:url" content={url} />
|
|
27127
|
-
<meta property="og:type" content="website" />
|
|
27128
|
-
|
|
27129
|
-
{/* Twitter */}
|
|
27130
|
-
<meta name="twitter:card" content="summary_large_image" />
|
|
27131
|
-
<meta name="twitter:title" content={title} />
|
|
27132
|
-
<meta name="twitter:description" content={description} />
|
|
27133
|
-
<meta name="twitter:image" content={image} />
|
|
27134
|
-
</Helmet>
|
|
27135
|
-
);
|
|
27136
|
-
}`,
|
|
27137
|
-
explanation: "Created SEOHead component using react-helmet-async for dynamic meta tags. Install: npm install react-helmet-async"
|
|
27138
|
-
};
|
|
27139
|
-
}
|
|
27775
|
+
Install: ${generatedCode.installCommands.join(" && ")}` : "";
|
|
27140
27776
|
return {
|
|
27141
|
-
issue: { code: "SPA_NO_META_MANAGEMENT", message: "SPA without meta management", severity: "warning" },
|
|
27142
|
-
file:
|
|
27777
|
+
issue: { code: "SPA_NO_META_MANAGEMENT", message: "SPA without dynamic meta tag management", severity: "warning" },
|
|
27778
|
+
file: generatedCode.file,
|
|
27143
27779
|
before: null,
|
|
27144
|
-
after:
|
|
27145
|
-
explanation:
|
|
27146
|
-
skipped: true,
|
|
27147
|
-
skipReason: `Framework-specific solution needed for ${framework.name}`
|
|
27780
|
+
after: generatedCode.code,
|
|
27781
|
+
explanation: generatedCode.explanation + installInstructions
|
|
27148
27782
|
};
|
|
27149
27783
|
}
|
|
27150
27784
|
function generatePreconnectFix(context) {
|
|
@@ -29988,6 +30622,9 @@ if (typeof globalThis !== "undefined") {
|
|
|
29988
30622
|
frameworks,
|
|
29989
30623
|
generateAICitableContent,
|
|
29990
30624
|
generateAllFixes,
|
|
30625
|
+
generateAngularSEOService,
|
|
30626
|
+
generateAstroBaseHead,
|
|
30627
|
+
generateAstroLayout,
|
|
29991
30628
|
generateAstroMeta,
|
|
29992
30629
|
generateBlogPost,
|
|
29993
30630
|
generateBranchName,
|
|
@@ -30012,22 +30649,35 @@ if (typeof globalThis !== "undefined") {
|
|
|
30012
30649
|
generateKeyFacts,
|
|
30013
30650
|
generateMarkdownReport,
|
|
30014
30651
|
generateNextAppMetadata,
|
|
30652
|
+
generateNextJsAppRouterMetadata,
|
|
30653
|
+
generateNextJsDynamicMetadata,
|
|
30654
|
+
generateNextJsPageMetadata,
|
|
30655
|
+
generateNextJsPagesRouterHead,
|
|
30656
|
+
generateNextJsRobots,
|
|
30657
|
+
generateNextJsSitemap,
|
|
30015
30658
|
generateNextPagesHead,
|
|
30659
|
+
generateNuxtPageExample,
|
|
30660
|
+
generateNuxtSEOHead,
|
|
30016
30661
|
generatePDFReport,
|
|
30017
30662
|
generatePRDescription,
|
|
30663
|
+
generateReactAppWrapper,
|
|
30018
30664
|
generateReactHelmetSocialMeta,
|
|
30665
|
+
generateReactSEOHead,
|
|
30019
30666
|
generateRecommendationQueries,
|
|
30020
30667
|
generateRemixMeta,
|
|
30021
30668
|
generateSecretsDoc,
|
|
30022
30669
|
generateSocialMetaFix,
|
|
30023
30670
|
generateSvelteKitMeta,
|
|
30671
|
+
generateSvelteKitSEOHead,
|
|
30024
30672
|
generateUncertaintyQuestions,
|
|
30673
|
+
generateVueSEOHead,
|
|
30025
30674
|
generateWizardQuestions,
|
|
30026
30675
|
generateWorkflow,
|
|
30027
30676
|
getAIVisibilitySummary,
|
|
30028
30677
|
getAutocompleteSuggestions,
|
|
30029
30678
|
getDateRange,
|
|
30030
30679
|
getExpandedSuggestions,
|
|
30680
|
+
getFrameworkSpecificFix,
|
|
30031
30681
|
getGSCSetupInstructions,
|
|
30032
30682
|
getGitUser,
|
|
30033
30683
|
getKeywordData,
|