← Files Willy SEOARCHIVED FILE

src/rules/schema.ts

5.2 KB · Oct 3, 2026 · 06:36 UTC

↓ Download file

import { AuditCheck, ExtractedPageData } from '../types.js';

export function auditSchema(data: ExtractedPageData): AuditCheck[] {
  const checks: AuditCheck[] = [];

  if (data.schemas.length === 0) {
    checks.push({
      id: 'schema-missing',
      category: 'schema',
      name: 'Datos Estructurados (Schema.org)',
      status: 'warning',
      score: 30,
      weight: 100,
      message: 'No se han detectado datos estructurados Schema.org en formato JSON-LD.',
      recommendation: 'Implementa marcado estructurado Schema (como Article, FAQPage, Product o Organization) para optar a Rich Snippets en Google.',
    });
  } else {
    const detectedTypes: string[] = [];

    for (const s of data.schemas) {
      if (s['@type']) {
        if (Array.isArray(s['@type'])) {
          detectedTypes.push(...s['@type']);
        } else {
          detectedTypes.push(s['@type']);
        }
      } else if (s['@graph'] && Array.isArray(s['@graph'])) {
        for (const node of s['@graph']) {
          if (node['@type']) {
            if (Array.isArray(node['@type'])) detectedTypes.push(...node['@type']);
            else detectedTypes.push(node['@type']);
          }
        }
      }
    }

    const uniqueTypes = Array.from(new Set(detectedTypes));

    if (uniqueTypes.length > 0) {
      checks.push({
        id: 'schema-detected',
        category: 'schema',
        name: 'Datos Estructurados (Schema.org)',
        status: 'good',
        score: 100,
        weight: 100,
        message: `Schema.org JSON-LD detectado correctamente. Tipos identificados: ${uniqueTypes.join(', ')}.`,
        snippet: JSON.stringify(data.schemas[0], null, 2),
      });
    } else {
      checks.push({
        id: 'schema-empty-type',
        category: 'schema',
        name: 'Datos Estructurados (Schema.org)',
        status: 'warning',
        score: 60,
        weight: 100,
        message: 'Se detectó script JSON-LD pero sin propiedad @type clara.',
        recommendation: 'Revisa la sintaxis del JSON-LD para asegurar que cumple el estándar de Schema.org.',
      });
    }
  }

  return checks;
}

/**
 * Generador automático de marcado Schema listo para usar
 */
export function generateSchemaTemplate(
  type: 'Article' | 'FAQPage' | 'Organization' | 'Product' | 'LocalBusiness',
  params: {
    url: string;
    title?: string;
    description?: string;
    faqs?: Array<{ question: string; answer: string }>;
  }
): string {
  switch (type) {
    case 'Article':
      return JSON.stringify(
        {
          '@context': 'https://schema.org',
          '@type': 'Article',
          headline: params.title || 'Título del artículo',
          description: params.description || 'Descripción del artículo',
          mainEntityOfPage: {
            '@type': 'WebPage',
            '@id': params.url,
          },
          author: {
            '@type': 'Person',
            name: 'Willy SEO',
          },
          publisher: {
            '@type': 'Organization',
            name: 'Willy SEO Consulting',
          },
          datePublished: new Date().toISOString(),
          dateModified: new Date().toISOString(),
        },
        null,
        2
      );

    case 'FAQPage':
      return JSON.stringify(
        {
          '@context': 'https://schema.org',
          '@type': 'FAQPage',
          mainEntity: (params.faqs || [
            {
              question: '¿Por qué es importante optimizar el SEO On-Page?',
              answer: 'El SEO On-Page ayuda a los motores de búsqueda a entender el contenido y su relevancia para los usuarios.',
            },
          ]).map(f => ({
            '@type': 'Question',
            name: f.question,
            acceptedAnswer: {
              '@type': 'Answer',
              text: f.answer,
            },
          })),
        },
        null,
        2
      );

    case 'LocalBusiness':
      return JSON.stringify(
        {
          '@context': 'https://schema.org',
          '@type': 'LocalBusiness',
          name: params.title || 'Nombre del Negocio',
          url: params.url,
          telephone: '+34 900 000 000',
          address: {
            '@type': 'PostalAddress',
            streetAddress: 'Calle Ejemplo 123',
            addressLocality: 'Madrid',
            postalCode: '28001',
            addressCountry: 'ES',
          },
          priceRange: '€€',
        },
        null,
        2
      );

    case 'Product':
      return JSON.stringify(
        {
          '@context': 'https://schema.org',
          '@type': 'Product',
          name: params.title || 'Nombre del Producto',
          description: params.description || 'Descripción del producto',
          offers: {
            '@type': 'Offer',
            priceCurrency: 'EUR',
            price: '49.99',
            availability: 'https://schema.org/InStock',
            url: params.url,
          },
        },
        null,
        2
      );

    case 'Organization':
    default:
      return JSON.stringify(
        {
          '@context': 'https://schema.org',
          '@type': 'Organization',
          name: params.title || 'Willy SEO',
          url: params.url,
          description: params.description || 'Asesoría y Consultoría SEO Estratégica',
        },
        null,
        2
      );
  }
}

SHA-256: 6dcb3c7a49956bed77da9adb470338d86cd397bd9adcc62c65abfdd2c96dd6e4