{"version":3,"file":"build-sitemap.js","sources":["../../src/build-sitemap.ts"],"sourcesContent":["import { writeFileSync } from 'node:fs'\nimport path from 'node:path'\nimport { create } from 'xmlbuilder2'\nimport { createLogger } from './utils'\nimport type { TanStackStartOutputConfig } from './schema'\nimport type { XMLBuilder } from 'xmlbuilder2/lib/interfaces'\n\nexport type SitemapUrl = {\n  loc: string\n  lastmod: string\n  priority?: number\n  changefreq?:\n    | 'always'\n    | 'hourly'\n    | 'daily'\n    | 'weekly'\n    | 'monthly'\n    | 'yearly'\n    | 'never'\n  alternateRefs?: Array<{\n    href: string\n    hreflang?: string\n  }>\n  images?: Array<{\n    loc: string\n    title?: string\n    caption?: string\n  }>\n  news?: {\n    publication: {\n      name: string\n      language: string\n    }\n    publicationDate: string | Date\n    title: string\n  }\n}\n\nexport type SitemapData = {\n  urls: Array<SitemapUrl>\n}\n\nfunction buildSitemapJson(\n  pages: TanStackStartOutputConfig['pages'],\n  host: string,\n): SitemapData {\n  const slash = checkSlash(host)\n\n  const urls: Array<SitemapUrl> = pages\n    .filter((page) => {\n      return page.sitemap?.exclude !== true\n    })\n    .map((page) => ({\n      loc: `${host}${slash}${page.path.replace(/^\\/+/g, '')}`,\n      lastmod: page.sitemap?.lastmod\n        ? new Date(page.sitemap.lastmod).toISOString().split('T')[0]!\n        : new Date().toISOString().split('T')[0]!,\n      priority: page.sitemap?.priority,\n      changefreq: page.sitemap?.changefreq,\n      alternateRefs: page.sitemap?.alternateRefs,\n      images: page.sitemap?.images,\n      news: page.sitemap?.news,\n    }))\n\n  return { urls }\n}\n\nfunction jsonToXml(sitemapData: SitemapData): string {\n  const sitemap = createXml('urlset')\n\n  for (const item of sitemapData.urls) {\n    const page = sitemap.ele('url')\n    page.ele('loc').txt(item.loc)\n    page.ele('lastmod').txt(item.lastmod)\n\n    if (item.priority !== undefined) {\n      page.ele('priority').txt(item.priority.toString())\n    }\n    if (item.changefreq) {\n      page.ele('changefreq').txt(item.changefreq)\n    }\n\n    // Add alternate references\n    if (item.alternateRefs?.length) {\n      for (const ref of item.alternateRefs) {\n        const alternateRef = page.ele('xhtml:link')\n        alternateRef.att('rel', 'alternate')\n        alternateRef.att('href', ref.href)\n        if (ref.hreflang) {\n          alternateRef.att('hreflang', ref.hreflang)\n        }\n      }\n    }\n\n    // Add images\n    if (item.images?.length) {\n      for (const image of item.images) {\n        const imageElement = page.ele('image:image')\n        imageElement.ele('image:loc').txt(image.loc)\n        if (image.title) {\n          imageElement.ele('image:title').txt(image.title)\n        }\n        if (image.caption) {\n          imageElement.ele('image:caption').txt(image.caption)\n        }\n      }\n    }\n\n    // Add news\n    if (item.news) {\n      const newsElement = page.ele('news:news')\n      const publication = newsElement.ele('news:publication')\n      publication.ele('news:name').txt(item.news.publication.name)\n      publication.ele('news:language').txt(item.news.publication.language)\n      newsElement\n        .ele('news:publication_date')\n        .txt(new Date(item.news.publicationDate).toISOString().split('T')[0]!)\n      newsElement.ele('news:title').txt(item.news.title)\n    }\n  }\n\n  return sitemap.end({ prettyPrint: true })\n}\n\nexport function buildSitemap({\n  startConfig,\n  publicDir,\n}: {\n  startConfig: TanStackStartOutputConfig\n  publicDir: string\n}) {\n  const logger = createLogger('sitemap')\n\n  let sitemapOptions = startConfig.sitemap\n\n  if (!sitemapOptions && startConfig.pages.length) {\n    sitemapOptions = { enabled: true, outputPath: 'sitemap.xml' }\n  }\n\n  if (!sitemapOptions?.enabled) {\n    throw new Error('Sitemap is not enabled')\n  }\n\n  const { host, outputPath } = sitemapOptions\n\n  if (!host) {\n    if (!startConfig.sitemap) {\n      logger.info(\n        'Hint: Pages found, but no sitemap host has been set. To enable sitemap generation, set the `sitemap.host` option.',\n      )\n      return\n    }\n    throw new Error(\n      'Sitemap host is not set and required to build the sitemap.',\n    )\n  }\n\n  if (!outputPath) {\n    throw new Error('Sitemap output path is not set')\n  }\n\n  const { pages } = startConfig\n\n  if (!pages.length) {\n    logger.info('No pages were found to build the sitemap. Skipping...')\n    return\n  }\n\n  logger.info('Building Sitemap...')\n\n  // Build the sitemap data\n  const sitemapData = buildSitemapJson(pages, host)\n\n  // Generate output paths\n  const xmlOutputPath = path.join(publicDir, outputPath)\n  const pagesOutputPath = path.join(publicDir, 'pages.json')\n\n  try {\n    // Write XML sitemap\n    logger.info(`Writing sitemap XML at ${xmlOutputPath}`)\n    writeFileSync(xmlOutputPath, jsonToXml(sitemapData))\n\n    // Write pages data for runtime use\n    logger.info(`Writing pages data at ${pagesOutputPath}`)\n    writeFileSync(\n      pagesOutputPath,\n      JSON.stringify(\n        {\n          pages,\n          host,\n          lastBuilt: new Date().toISOString(),\n        },\n        null,\n        2,\n      ),\n    )\n  } catch (e) {\n    logger.error(`Unable to write sitemap files`, e)\n  }\n}\n\nfunction createXml(elementName: 'urlset' | 'sitemapindex'): XMLBuilder {\n  return create({ version: '1.0', encoding: 'UTF-8' })\n    .ele(elementName, {\n      xmlns: 'https://www.sitemaps.org/schemas/sitemap/0.9',\n    })\n    .com(`This file was automatically generated by TanStack Start.`)\n}\n\nfunction checkSlash(host: string): string {\n  const finalChar = host.slice(-1)\n  return finalChar === '/' ? '' : '/'\n}\n"],"names":[],"mappings":"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"}