UNPKG

@forge42/seo-tools

Version:

Framework agnostic set of helpers designed to help you create, maintain and develop your SEO

1 lines 6.36 kB
{"version":3,"sources":["../../src/remix/sitemap.ts"],"sourcesContent":["import type { Route, RouteManifest, ServerRoute } from \"@remix-run/server-runtime/dist/routes.js\"\nimport { type SitemapEntry, type SitemapRoute, generateSitemap } from \"../sitemap\"\n\nexport type SitemapFunctionReturnData = SitemapEntry | SitemapEntry[]\n\nexport type SitemapFunctionReturn = Promise<SitemapFunctionReturnData> | SitemapFunctionReturnData\n\nexport interface SitemapHandle<T> {\n\tsitemap: SitemapFunction<T>\n\t[key: string]: unknown\n}\n\nexport type SitemapFunction<T> = (\n\tdomain: string,\n\turl: string,\n\tsitemapData: T\n) => Promise<SitemapFunctionReturnData> | SitemapFunctionReturnData\n\nconst convertRemixPathToUrl = (routes: RouteManifest<Route>, route: Route) => {\n\tlet currentRoute: Route | null = route\n\tconst path = []\n\n\twhile (currentRoute) {\n\t\tpath.push(currentRoute.path)\n\t\tif (!currentRoute.parentId) break\n\t\tif (!routes[currentRoute.parentId]) break\n\t\tcurrentRoute = routes[currentRoute.parentId]\n\t}\n\tconst output = path.reverse().filter(Boolean).join(\"/\")\n\treturn output === \"\" ? \"/\" : output\n}\n\nconst createExtendedRoutes = (routes: RouteManifest<ServerRoute>) => {\n\treturn Object.values(routes).map((route) => {\n\t\treturn {\n\t\t\t...route,\n\t\t\turl: convertRemixPathToUrl(routes, route),\n\t\t}\n\t})\n}\nconst generateRemixSitemapRoutes = async ({\n\tdomain,\n\tsitemapData,\n\troutes,\n}: {\n\tdomain: string\n\tsitemapData?: unknown\n\troutes?: RouteManifest<ServerRoute>\n}) => {\n\tlet finalRoutes = routes\n\tif (!finalRoutes) {\n\t\t// @ts-expect-error - This import exists but is not picked up by the typescript compiler because it's a remix internal\n\t\tconst { routes } = await import(\"virtual:remix/server-build\").catch(() => {\n\t\t\tthrow new Error(\n\t\t\t\t\"Could not find the remix server build. Make sure you have Remix running on Vite and not in SPA mode. Otherwise use the generateSitemap utility.\"\n\t\t\t)\n\t\t})\n\t\tfinalRoutes = routes\n\t}\n\t// Add the url to each route\n\tconst extendedRoutes = createExtendedRoutes(finalRoutes as unknown as RouteManifest<ServerRoute>)\n\n\tconst transformedRoutes = await Promise.all(\n\t\textendedRoutes.map(async (route) => {\n\t\t\tconst url = route.url\n\t\t\t// We don't want to include the root route in the sitemap\n\t\t\tif (route.id === \"root\") return\n\t\t\t// If the route has a module, get the handle\n\t\t\tconst handle = route.module.handle\n\t\t\t// If the route has a sitemap function, call it and return the sitemap entries\n\t\t\tif (handle && typeof handle === \"object\" && \"sitemap\" in handle && typeof handle.sitemap === \"function\") {\n\t\t\t\t// Type the function just in case\n\t\t\t\tconst sitemap = handle.sitemap as SitemapFunction<unknown>\n\t\t\t\tconst sitemapEntries: SitemapFunctionReturnData = await sitemap(domain, url, sitemapData)\n\t\t\t\treturn { url, sitemapEntries, id: route.id }\n\t\t\t}\n\t\t\t// Otherwise, just return the route as a single entry\n\t\t\treturn { url, sitemapEntries: null, id: route.id }\n\t\t})\n\t)\n\t// Filter out any undefined routes\n\treturn transformedRoutes.filter(Boolean) as SitemapRoute[]\n}\n\nexport interface RemixSitemapInfo {\n\t/**\n\t * The domain to append the urls to\n\t * @example \"https://example.com\"\n\t */\n\tdomain: string\n\t/**\n\t * Any data you want to pass to the sitemap functions used in the handle exports\n\t * @example { lastUpdated: new Date() }\n\t */\n\tsitemapData?: unknown\n\t/**\n\t * An array of patterns to ignore (e.g. [\"/status\"])\n\t * @example [\"/status\"]\n\t */\n\tignore?: string[]\n\t/**\n\t * A function to transform the url before adding it to the domain\n\t * @example (url) => url.replace(/\\/$/, \"\")\n\t */\n\turlTransformer?: (url: string) => string\n\n\t/**\n\t * The routes object from the remix server build. If not provided, the utility will try to import it.\n\t */\n\troutes?: RouteManifest<ServerRoute>\n}\n\n/**\n * Helper method used to generate a sitemap from all the remix routes in the project.\n *\n * By default ignores all xml and txt files and any route that matches the pattern \"sitemap*\"\n *\n *\n * @param sitemapInfo- Object containing the domain, sitemapData, ignore and urlTransformer\n * @throws Error if the remix server build is not found\n * @returns Sitemap string to be passed back to the response.\n */\nexport const generateRemixSitemap = async (sitemapInfo: RemixSitemapInfo) => {\n\tconst { domain, sitemapData, ignore, urlTransformer, routes } = sitemapInfo\n\tconst finalRoutes = await generateRemixSitemapRoutes({ domain, sitemapData, routes })\n\treturn generateSitemap({ domain, routes: finalRoutes, ignore, urlTransformer })\n}\n"],"mappings":"0CAkBA,IAAMA,EAAwB,CAACC,EAA8BC,IAAiB,CAC7E,IAAIC,EAA6BD,EAC3BE,EAAO,CAAC,EAEd,KAAOD,IACNC,EAAK,KAAKD,EAAa,IAAI,EACvB,GAACA,EAAa,UACd,CAACF,EAAOE,EAAa,QAAQ,KACjCA,EAAeF,EAAOE,EAAa,QAAQ,EAE5C,IAAME,EAASD,EAAK,QAAQ,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,EACtD,OAAOC,IAAW,GAAK,IAAMA,CAC9B,EAEMC,EAAwBL,GACtB,OAAO,OAAOA,CAAM,EAAE,IAAKC,IAC1B,CACN,GAAGA,EACH,IAAKF,EAAsBC,EAAQC,CAAK,CACzC,EACA,EAEIK,EAA6B,MAAO,CACzC,OAAAC,EACA,YAAAC,EACA,OAAAR,CACD,IAIM,CACL,IAAIS,EAAcT,EAClB,GAAI,CAACS,EAAa,CAEjB,GAAM,CAAE,OAAAT,CAAO,EAAI,KAAM,QAAO,4BAA4B,EAAE,MAAM,IAAM,CACzE,MAAM,IAAI,MACT,iJACD,CACD,CAAC,EACDS,EAAcT,CACf,CAEA,IAAMU,EAAiBL,EAAqBI,CAAoD,EAqBhG,OAnB0B,MAAM,QAAQ,IACvCC,EAAe,IAAI,MAAOT,GAAU,CACnC,IAAMU,EAAMV,EAAM,IAElB,GAAIA,EAAM,KAAO,OAAQ,OAEzB,IAAMW,EAASX,EAAM,OAAO,OAE5B,GAAIW,GAAU,OAAOA,GAAW,UAAY,YAAaA,GAAU,OAAOA,EAAO,SAAY,WAAY,CAExG,IAAMC,EAAUD,EAAO,QACjBE,EAA4C,MAAMD,EAAQN,EAAQI,EAAKH,CAAW,EACxF,MAAO,CAAE,IAAAG,EAAK,eAAAG,EAAgB,GAAIb,EAAM,EAAG,CAC5C,CAEA,MAAO,CAAE,IAAAU,EAAK,eAAgB,KAAM,GAAIV,EAAM,EAAG,CAClD,CAAC,CACF,GAEyB,OAAO,OAAO,CACxC,EAwCac,EAAuB,MAAOC,GAAkC,CAC5E,GAAM,CAAE,OAAAT,EAAQ,YAAAC,EAAa,OAAAS,EAAQ,eAAAC,EAAgB,OAAAlB,CAAO,EAAIgB,EAC1DP,EAAc,MAAMH,EAA2B,CAAE,OAAAC,EAAQ,YAAAC,EAAa,OAAAR,CAAO,CAAC,EACpF,OAAOmB,EAAgB,CAAE,OAAAZ,EAAQ,OAAQE,EAAa,OAAAQ,EAAQ,eAAAC,CAAe,CAAC,CAC/E","names":["convertRemixPathToUrl","routes","route","currentRoute","path","output","createExtendedRoutes","generateRemixSitemapRoutes","domain","sitemapData","finalRoutes","extendedRoutes","url","handle","sitemap","sitemapEntries","generateRemixSitemap","sitemapInfo","ignore","urlTransformer","generateSitemap"]}