sicua
Version:
A tool for analyzing project structure and dependencies
374 lines (373 loc) • 9.88 kB
TypeScript
export interface SEOAnalysisResult {
metaTags: {
pages: Record<string, MetaTagAnalysis>;
statistics: MetaTagStatistics;
};
semanticStructure: {
headingHierarchy: HeadingAnalysis;
landmarkElements: LandmarkUsage;
accessibility: StaticA11yAnalysis;
};
contentStructure: {
textContent: TextContentAnalysis;
structuredData: StructuredDataAnalysis;
routing: StaticRoutingAnalysis;
};
appRouter: {
specialFiles: AppRouterSpecialFiles;
layoutHierarchy: LayoutHierarchy;
routeGroups: RouteGroupAnalysis;
parallelRoutes: ParallelRouteAnalysis;
};
performance: {
coreWebVitals: CoreWebVitalsAnalysis;
bundleOptimization: BundleOptimizationAnalysis;
lazyLoading: LazyLoadingAnalysis;
};
imageOptimization: ImageOptimizationAnalysis;
middleware: MiddlewareAnalysis;
}
export interface MetaTagAnalysis {
title: {
present: boolean;
length: number;
isDynamic: boolean;
};
description: {
present: boolean;
length: number;
isDynamic: boolean;
};
robots: {
present: boolean;
directives: string[];
};
canonical: {
present: boolean;
value: string;
isDynamic: boolean;
};
openGraph: {
present: boolean;
properties: {
title: boolean;
description: boolean;
image: boolean;
url: boolean;
};
};
twitter: {
present: boolean;
properties: {
card: boolean;
title: boolean;
description: boolean;
image: boolean;
};
};
viewport: {
present: boolean;
isResponsive: boolean;
};
}
export interface MetaTagStatistics {
pagesWithTitle: number;
pagesWithDescription: number;
pagesWithCanonical: number;
averageTitleLength: number;
averageDescriptionLength: number;
pagesWithSocialMeta: number;
duplicateTitles: Array<{
title: string;
pages: string[];
}>;
}
export interface HeadingAnalysis {
hierarchyIssues: Array<{
page: string;
path: string;
issue: "missing-h1" | "multiple-h1" | "skipped-level";
}>;
statistics: {
pagesWithH1: number;
averageHeadingsPerPage: number;
headingLevelDistribution: Record<"h1" | "h2" | "h3" | "h4" | "h5" | "h6", number>;
};
}
export interface LandmarkUsage {
elements: {
header: number;
main: number;
footer: number;
nav: number;
aside: number;
article: number;
section: number;
};
coverage: {
pagesWithAllLandmarks: number;
pagesWithHeader: number;
pagesWithMain: number;
pagesWithFooter: number;
};
}
export interface StaticA11yAnalysis {
images: {
totalImages: number;
missingAlt: number;
emptyAlt: number;
decorativeImages: number;
};
aria: {
totalAttributes: number;
byAttribute: Record<string, number>;
potentialMisuse: Array<{
element: string;
attribute: string;
issue: string;
}>;
};
buttons: {
total: number;
missingText: number;
onlyIconButtons: number;
};
forms: {
inputs: {
total: number;
missingLabels: number;
missingAriaLabels: number;
};
};
}
export interface TextContentAnalysis {
byPage: Record<string, {
textLength: number;
textToMarkupRatio: number;
paragraphCount: number;
listCount: number;
}>;
statistics: {
averageTextLength: number;
averageTextToMarkupRatio: number;
};
}
export interface StructuredDataAnalysis {
schemas: Array<{
type: string;
coverage: number;
location: string;
}>;
statistics: {
totalSchemas: number;
schemasPerPage: number;
commonTypes: Array<{
type: string;
count: number;
}>;
};
}
export interface StaticRoutingAnalysis {
routes: Array<{
path: string;
isDynamic: boolean;
params?: string[];
component: string;
}>;
internalLinks: Array<{
from: string;
to: string;
isRelative: boolean;
}>;
statistics: {
totalRoutes: number;
dynamicRoutes: number;
averageLinksPerPage: number;
orphanedPages: string[];
};
}
export interface AppRouterSpecialFiles {
files: Array<{
type: "layout" | "loading" | "error" | "not-found" | "global-error" | "template";
path: string;
routeSegment: string;
hasMetadata: boolean;
hasGenerateMetadata: boolean;
issues: string[];
}>;
coverage: {
routesWithLayout: number;
routesWithLoading: number;
routesWithError: number;
routesWithNotFound: number;
};
statistics: {
totalSpecialFiles: number;
specialFilesByType: Record<string, number>;
averageSpecialFilesPerRoute: number;
};
}
export interface LayoutHierarchy {
layouts: Array<{
path: string;
routeSegment: string;
level: number;
parentLayout: string | null;
childLayouts: string[];
hasMetadata: boolean;
metadataConflicts: string[];
children: string[];
}>;
statistics: {
totalLayouts: number;
maxNestingLevel: number;
averageChildrenPerLayout: number;
layoutsWithMetadataConflicts: number;
};
}
export interface RouteGroupAnalysis {
routeGroups: Array<{
name: string;
path: string;
routes: string[];
hasLayout: boolean;
hasSpecialFiles: string[];
}>;
statistics: {
totalRouteGroups: number;
routesInGroups: number;
ungroupedRoutes: number;
};
}
export interface ParallelRouteAnalysis {
parallelRoutes: Array<{
name: string;
path: string;
parentRoute: string;
defaultSlot: boolean;
hasDefault: boolean;
routes: string[];
}>;
statistics: {
totalParallelRoutes: number;
parallelRoutesWithDefaults: number;
routesWithParallelSegments: number;
};
}
export interface CoreWebVitalsAnalysis {
potentialIssues: Array<{
type: "LCP" | "CLS" | "FID" | "INP";
severity: "low" | "medium" | "high";
description: string;
location: string;
suggestion: string;
}>;
optimizations: {
imageOptimization: number;
lazyLoadingCandidates: number;
largeBundles: string[];
blockingResources: string[];
};
statistics: {
totalPotentialIssues: number;
issuesByType: Record<string, number>;
pagesWithIssues: number;
};
}
export interface BundleOptimizationAnalysis {
heavyImports: Array<{
importPath: string;
importedBy: string[];
isDynamic: boolean;
isServerComponent: boolean;
potentialImpact: "low" | "medium" | "high";
}>;
criticalResources: Array<{
type: "font" | "image" | "script" | "style";
path: string;
usedInPages: string[];
isPreloaded: boolean;
isOptimized: boolean;
}>;
statistics: {
totalHeavyImports: number;
dynamicImportsUsage: number;
staticImportsInPages: number;
criticalResourcesCount: number;
};
}
export interface LazyLoadingAnalysis {
components: Array<{
name: string;
path: string;
usedInPages: string[];
isLazyLoaded: boolean;
lazyLoadingMethod: "React.lazy" | "next/dynamic" | "none";
shouldBeLazyLoaded: boolean;
reason: string;
}>;
images: Array<{
path: string;
usedInPages: string[];
hasLazyLoading: boolean;
isNextImage: boolean;
isAboveFold: boolean | null;
}>;
statistics: {
totalComponents: number;
lazyLoadedComponents: number;
totalImages: number;
lazyLoadedImages: number;
componentsRecommendedForLazyLoading: number;
};
}
export interface ImageOptimizationAnalysis {
images: Array<{
type: "next/image" | "img" | "background-image";
path: string | null;
usedInPages: string[];
attributes: {
alt: string | null;
width: number | null;
height: number | null;
priority: boolean;
placeholder: string | null;
sizes: string | null;
fill: boolean;
};
issues: Array<{
type: "missing-alt" | "missing-dimensions" | "no-optimization" | "large-size" | "wrong-format";
severity: "low" | "medium" | "high";
description: string;
}>;
seoScore: number;
}>;
statistics: {
totalImages: number;
nextImageUsage: number;
imagesWithIssues: number;
averageSeoScore: number;
imagesByFormat: Record<string, number>;
};
recommendations: string[];
}
export interface MiddlewareAnalysis {
hasMiddleware: boolean;
middlewareFile: {
path: string | null;
hasMatchers: boolean;
matchers: string[];
affectedRoutes: string[];
seoImpacts: Array<{
type: "redirect" | "rewrite" | "header-modification" | "authentication" | "i18n";
severity: "low" | "medium" | "high";
description: string;
recommendation: string;
}>;
};
statistics: {
totalSeoImpacts: number;
affectedRoutes: number;
highSeverityIssues: number;
};
}