super-sitemap

repository·main·Indexed 42 days ago

https://github.com/jasongitmail/super-sitemap

A sitemap library for TanStack Start and SvelteKit designed to automate route discovery and XML sitemap generation. It handles parameterized routes, i18n, and provides framework-specific adapters to ensure paths are correctly indexed without manual entry.

Tokens
11.2K
Snippets
33
Records
45
Agent score
55%

What's inside super-sitemap

  1. When to use `processPaths()` vs built-in options

    main

    Before using processPaths(), check if a built-in configuration option is more appropriate and performant:

    GoalRecommended Option
    Exclude entire route patternsexcludeRoutePatterns
    Append known extra pathsadditionalPaths
    Set lastmod, changefreq, or priority per pathparamValues
    Exclude specific final paths (e.g., only /zh/about but keep /en/about)processPaths()

    Note: excludeRoutePatterns is more performant because it runs before path generation, whereas processPaths() runs after paths have been fully expanded.

  2. How SvelteKit route discovery works

    main

    The SvelteKit adapter discovers routes by using Vite's import.meta.glob to scan for page files: /src/routes/**/+page*.{svelte,md,svx}.

    Because it uses a build-time manifest, it supports both prerendered and runtime sitemaps. It automatically parses SvelteKit routing conventions, including:

    • Route groups: (group)
    • Parameters: [param], [[optional]], [param=matcher], and [...rest]
    • Locale conventions: [[locale]] or [locale]

    Server Route Exclusion: SvelteKit endpoints (e.g., +server.ts) are never included because the discovery glob specifically targets +page files.

  3. How i18n routing works in Super Sitemap

    main

    Super Sitemap identifies locale routes by looking for a route parameter named locale. The behavior of generated URLs depends on whether the locale route is optional or required in your framework:

    Optional Locale Routes

    Optional routes omit the default locale from the generated URLs.

    • SvelteKit: Use [[locale]] syntax.
    • TanStack Start: Use {-$locale} syntax.
    • Example Output: /about (default) and /de/about (alternate).

    Required Locale Routes

    Required routes include the locale segment for every locale, including the default.

    • SvelteKit: Use [locale] syntax.
    • TanStack Start: Use $locale syntax.
    • Example Output: /en/about and /de/about.
    • Note: If using required routes, you must manually redirect / to a locale-specific root (like /en) because the root / is not generated.
    | Framework      | Optional locale route | Required locale route |
    | -------------- | --------------------- | --------------------- |
    | SvelteKit      | `[[locale]]`          | `[locale]`            |
    | TanStack Start | `{-$locale}`          | `$locale`             |
  4. Provide data for parameterized routes via `paramValues`

    main

    When your routes contain parameters (e.g., /blog/$slug or /blog/[slug]), you must provide the actual values via the paramValues option to prevent errors and ensure all pages are indexed.

    paramValues supports three formats:

    1. 1D Array of strings: For simple single-parameter routes.
    2. 2D Array of strings: For nested parameters (e.g., ['usa', 'new-york']).
    3. Array of ParamValue objects: For fine-grained control over individual entries.

    Each ParamValue object can include:

    • values (Required): An array of strings representing the parameter values.
    • lastmod (Optional): ISO string for the last modification date.
    • changefreq (Optional): Frequency of change.
    • priority (Optional): Relative importance of the URL.
    paramValues: {
      // 1D array
      '/blog/$slug': ['hello-world', 'another-post'],
    
      // 2D array
      '/campsites/$country/$state': [
        ['usa', 'new-york'],
        ['usa', 'california'],
      ],
    
      // Array of ParamValue objects
      '/athlete-rankings/$country/$state': [
        {
          values: ['usa', 'new-york'],
          lastmod: '2025-01-01T00:00:00Z',
          changefreq: 'daily',
          priority: 0.5,
        },
      ],
    }
  5. How Super Sitemap is architected

    main

    Super Sitemap uses a layered architecture consisting of framework-specific Adapters and a framework-agnostic Core.

    The Layers

    1. Adapters (sveltekit | tanstack-start)

      • Responsibility: Discovering routes using the framework's native mechanisms and parsing framework-specific syntax into a normalized format (NormalizedRoute[]).
      • Note: Adapters do not perform XML rendering or pagination; they only handle route discovery and normalization.
    2. Core (src/core/internal/)

      • Responsibility: Path generation, i18n expansion, deduplication, sorting, pagination, XML rendering, and Response building.
      • Constraint: The core has zero framework imports and zero Node.js built-ins, making it highly portable.

    The Boundary

    All communication between the Adapter and the Core happens via standardized data structures: NormalizedRoute[] or PathObj[]. This ensures the core remains decoupled from framework-specific routing logic.

  6. How Super Sitemap handles optional params

    main

    Super Sitemap treats optional parameters as multiple distinct route variants rather than a single route. When a route contains optional parameters, it expands into every possible URL shape supported by that definition.

    For example, a route with two consecutive optional parameters expands into three variants:

    1. Base: The route without any parameters (e.g., /foo).
    2. Shorter: The route with one parameter (e.g., /foo/a).
    3. Longest: The route with both parameters (e.g., /foo/a/b).

    Because these are treated as separate keys, you must explicitly provide values for dynamic variants using paramValues or remove them using excludeRoutePatterns to ensure the sitemap is generated correctly.

    /* Example of expansion for a route with two optional params */
    // Base: `/foo` 
    // Shorter: `/foo/a` 
    // Longest: `/foo/a/b`
  7. How TanStack Start route discovery works

    main

    The TanStack Start adapter does not read files. Instead, it requires the consumer to pass their app's getRouter function. The adapter then reads the resolved router.routesByPath map.

    Supported Route Styles

    The adapter supports all TanStack Router definition styles (file-based, code-based, or virtual) because it consumes the resolved router instance. It parses:

    • Parameters: $param
    • Optional parameters: {-$optional}
    • Splat/Rest: $
    • Locale routes: $locale or {-$locale}

    Server Route Exclusion

    Endpoints are automatically excluded by inspecting the resolved route's options. A route is considered server-only (and thus excluded) if it declares options.server and has no options.component. This ensures the sitemap never lists itself or other API endpoints without requiring manual excludeRoutePatterns configuration.

  8. Understand the super-sitemap package structure and distribution

    main

    The super-sitemap package is distributed as a pre-compiled JavaScript package located in the dist/ directory. It is designed to be edge-runtime safe and contains no Node.js built-ins (like node:fs) in its shipped code.

    Key architectural characteristics for consumers:

    • Framework Agnostic Core: The engine is decoupled from framework-specific logic.
    • Structural Typing: Adapters use structural typing rather than importing framework packages as peer dependencies, making the library lightweight.
    • Vite-Native: The library ships import.meta.glob patterns verbatim. These are not transformed by the library itself but are transformed by your consumer application's Vite bundler at build time.
    • No Framework Dependencies: The library does not contain .svelte files or require framework-specific compilers to build; it uses standard tsc compilation.
  9. Understand the data flow in Super Sitemap

    main

    The sitemap generation follows a specific pipeline:

    1. Route Normalization: The Adapter parses framework routes into NormalizedRoute[].
    2. Path Preparation: core.preparePaths() takes the normalized routes, paramValues, locales, and defaults to generate a list of concrete paths.
      • Pipeline Order: Interpolate normalized routes $\rightarrow$ append additionalPaths $\rightarrow$ run processPaths() callback $\rightarrow$ deduplicate (last occurrence wins) $\rightarrow$ sort (if sort: 'alpha' is set).
    3. Output Generation: The resulting PathObj[] is used for:
      • XML/Response: core.getBody() or core.response() handles pagination, XML rendering, and headers.
      • SEO Testing: selectSamplePaths() picks one concrete path per route shape for smoke testing.
  10. Enable Sitemap Index support

    main

    If your website has more than 50,000 URLs, you should enable Sitemap Index support to comply with sitemaps.org recommendations.

    To enable this, you must:

    1. Configure your routing so that the sitemap route can serve both the main /sitemap.xml and paginated files like /sitemap1.xml, /sitemap2.xml, etc.
    2. Pass the page parameter from your route params into the response function in your sitemap configuration.

    Once configured, the sitemap.xml route will automatically return a <sitemapindex> if the total URL count exceeds maxPerPage (defaulting to 50,000), or a standard sitemap if it does not. You can also choose to always treat your sitemap as an index for optimal performance regardless of URL count.

    // Example logic for TanStack Start
    // /src/routes/sitemap{-$page}[.]xml.ts
    import { createFileRoute } from '@tanstack/react-router';
    import { response } from 'super-sitemap/tanstack-start';
    
    export const Route = createFileRoute('/sitemap{-$page}.xml')({
      server: {
        handlers: {
          GET: ({ params }) =>
            response({
              origin: 'https://example.com',
              router: getRouter,
              page: params.page,
            }),
        },
      },
    });
  11. Fetch param values from a database

    main

    When generating sitemaps, you often need to query a database to populate paramValues. Use SELECT DISTINCT to avoid duplicate entries for routes with multiple rows sharing the same parameters.

    Once fetched, convert the database rows into a 2D array (for multiple params) or a 1D array (for single params) to match the expected paramValues format.

    // 1. Query DB (Example SQL)
    // SELECT DISTINCT LOWER(country), LOWER(state) FROM campsites;
    
    // 2. Convert to 2D array for paramValues
    const arrayOfArrays = resultFromDB.map((row) => Object.values(row));
    // Result: [['usa','new-york'],['usa', 'california']]
    
    // 3. Use in config
    paramValues: {
      '/campsites/$country/$state': arrayOfArrays
    }
  12. Migrate from super-sitemap v1 to v2

    main

    If you are upgrading from version 1 to version 2, you must apply the following breaking changes:

    1. Framework-specific imports: Use the new entry points for your framework:
      • SvelteKit: import * as sitemap from 'super-sitemap/sveltekit'
      • TanStack Start: import * as sitemap from 'super-sitemap/tanstack-start'
    2. Locale Configuration: The lang option has been renamed to locales. Use the following shape: locales: { default: 'en', alternates: ['de'] }
    3. Dynamic Locale Params: Route parameters used for locales must be named locale:
      • SvelteKit: Use [[locale]] (instead of [[lang]]).
      • TanStack Start: Use {-$locale}.
    4. Regex Patterns: excludeRoutePatterns now requires JavaScript RegExp literals instead of strings (e.g., /^\/dashboard/ instead of "^/dashboard").
    5. Pattern Matching: excludeRoutePatterns matches against valid paramValues keys; route groups like (authenticated) cannot be matched.
    6. Utility Changes: sampledUrls() and sampledPaths() have been removed. Use getSamplePaths() instead.