remix-seo-plus
Version:
Collection of SEO utilities like sitemap, robots.txt, etc. for a Remix application. A fork of https://github.com/balavishnuvj/remix-seo with some added bug fixes and features.
103 lines (102 loc) • 3.79 kB
JavaScript
;
// This is adapted from https://github.com/kentcdodds/kentcdodds.com
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getSitemapXml = void 0;
const isEqual_1 = __importDefault(require("lodash/isEqual"));
function typedBoolean(value) {
return Boolean(value);
}
function removeTrailingSlash(s) {
return s.endsWith("/") ? s.slice(0, -1) : s;
}
async function getSitemapXml(args, routes, options) {
const { siteUrl } = options;
function getEntry({ route, lastmod, changefreq, priority = 0.7, }) {
return `
<url>
<loc>${siteUrl}${route}</loc>
${lastmod ? `<lastmod>${lastmod}</lastmod>` : ""}
${changefreq ? `<changefreq>${changefreq}</changefreq>` : ""}
${typeof priority === "number" ? `<priority>${priority}</priority>` : ""}
</url>
`.trim();
}
const rawSitemapEntries = (await Promise.all(Object.entries(routes).map(async ([id, { module: mod }]) => {
if (id === "root")
return;
const handle = mod.handle;
if (handle === null || handle === void 0 ? void 0 : handle.getSitemapEntries) {
return handle.getSitemapEntries(args);
}
// exclude resource routes from the sitemap
// (these are an opt-in via the getSitemapEntries method)
if (!("default" in mod))
return;
const manifestEntry = routes[id];
if (!manifestEntry) {
console.warn(`Could not find a manifest entry for ${id}`);
return;
}
let parentId = manifestEntry.parentId;
let parent = parentId ? routes[parentId] : null;
let path;
if (manifestEntry.path) {
path = removeTrailingSlash(manifestEntry.path);
}
else if (manifestEntry.index) {
path = "";
}
else {
return;
}
while (parent) {
// the root path is '/', so it messes things up if we add another '/'
const parentPath = parent.path
? removeTrailingSlash(parent.path)
: "";
if (!path.startsWith("/")) {
path = `${parentPath}/${path}`;
}
parentId = parent.parentId;
parent = parentId ? routes[parentId] : null;
}
// we can't handle dynamic routes, so if the handle doesn't have a
// getSitemapEntries function, we just
if (path.includes(":"))
return;
if (path.includes("*"))
return;
if (id === "root")
return;
const entry = { route: removeTrailingSlash(path) };
return entry;
})))
.flatMap((z) => z)
.filter(typedBoolean);
const sitemapEntries = [];
for (const entry of rawSitemapEntries) {
const existingEntryForRoute = sitemapEntries.find((e) => e.route === entry.route);
if (existingEntryForRoute) {
if (!(0, isEqual_1.default)(existingEntryForRoute, entry)) {
console.warn(`Duplicate route for ${entry.route} with different sitemap data`, { entry, existingEntryForRoute });
}
}
else {
sitemapEntries.push(entry);
}
}
return `
<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
>
${sitemapEntries.map((entry) => getEntry(entry)).join("")}
</urlset>
`.trim();
}
exports.getSitemapXml = getSitemapXml;