203 lines
7.2 KiB
JavaScript
203 lines
7.2 KiB
JavaScript
import path from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { ZodError } from "zod/v4";
|
|
import { generateSitemap } from "./generate-sitemap.js";
|
|
import { validateOptions } from "./validate-options.js";
|
|
import { writeSitemap } from "./write-sitemap.js";
|
|
import { writeSitemapChunk } from "./write-sitemap-chunk.js";
|
|
import { EnumChangefreq } from "sitemap";
|
|
function formatConfigErrorMessage(err) {
|
|
const errorList = err.issues.map((issue) => ` ${issue.path.join(".")} ${issue.message + "."}`);
|
|
return errorList.join("\n");
|
|
}
|
|
const PKG_NAME = "@astrojs/sitemap";
|
|
const STATUS_CODE_PAGES = /* @__PURE__ */ new Set(["404", "500"]);
|
|
const isStatusCodePage = (locales) => {
|
|
const statusPathNames = new Set(
|
|
locales.flatMap((locale) => [...STATUS_CODE_PAGES].map((page) => `${locale}/${page}`)).concat([...STATUS_CODE_PAGES])
|
|
);
|
|
return (pathname) => {
|
|
if (pathname.endsWith("/")) {
|
|
pathname = pathname.slice(0, -1);
|
|
}
|
|
if (pathname.startsWith("/")) {
|
|
pathname = pathname.slice(1);
|
|
}
|
|
return statusPathNames.has(pathname);
|
|
};
|
|
};
|
|
const createPlugin = (options) => {
|
|
let _routes;
|
|
let config;
|
|
return {
|
|
name: PKG_NAME,
|
|
hooks: {
|
|
"astro:routes:resolved": ({ routes }) => {
|
|
_routes = routes;
|
|
},
|
|
"astro:config:done": async ({ config: cfg }) => {
|
|
config = cfg;
|
|
},
|
|
"astro:build:done": async ({ dir, pages, logger }) => {
|
|
try {
|
|
if (!config.site) {
|
|
logger.warn(
|
|
"The Sitemap integration requires the `site` astro.config option. Skipping."
|
|
);
|
|
return;
|
|
}
|
|
const opts = validateOptions(config.site, options);
|
|
const {
|
|
filenameBase,
|
|
filter,
|
|
customPages,
|
|
customSitemaps,
|
|
serialize,
|
|
entryLimit,
|
|
chunks
|
|
} = opts;
|
|
const outFile = `${filenameBase}-index.xml`;
|
|
const finalSiteUrl = new URL(config.base, config.site);
|
|
const shouldIgnoreStatus = isStatusCodePage(Object.keys(opts.i18n?.locales ?? {}));
|
|
let pageUrls = pages.filter((p) => !shouldIgnoreStatus(p.pathname)).map((p) => {
|
|
if (p.pathname !== "" && !finalSiteUrl.pathname.endsWith("/"))
|
|
finalSiteUrl.pathname += "/";
|
|
if (p.pathname.startsWith("/")) p.pathname = p.pathname.slice(1);
|
|
const fullPath = finalSiteUrl.pathname + p.pathname;
|
|
return new URL(fullPath, finalSiteUrl).href;
|
|
});
|
|
const addRouteUrl = (urls, r) => {
|
|
if (r.pathname) {
|
|
if (shouldIgnoreStatus(r.pathname ?? r.pattern)) return;
|
|
let fullPath = finalSiteUrl.pathname;
|
|
if (fullPath.endsWith("/")) fullPath += r.generate(r.pathname).substring(1);
|
|
else fullPath += r.generate(r.pathname);
|
|
const newUrl = new URL(fullPath, finalSiteUrl).href;
|
|
if (config.trailingSlash === "never") {
|
|
urls.push(newUrl);
|
|
} else if (config.build.format === "directory" && !newUrl.endsWith("/")) {
|
|
urls.push(newUrl + "/");
|
|
} else {
|
|
urls.push(newUrl);
|
|
}
|
|
}
|
|
};
|
|
const routeUrls = _routes.reduce((urls, r) => {
|
|
if (r.type !== "page") return urls;
|
|
addRouteUrl(urls, r);
|
|
for (const fallbackRoute of r.fallbackRoutes ?? []) {
|
|
addRouteUrl(urls, fallbackRoute);
|
|
}
|
|
return urls;
|
|
}, []);
|
|
pageUrls = Array.from(/* @__PURE__ */ new Set([...pageUrls, ...routeUrls, ...customPages ?? []]));
|
|
if (filter) {
|
|
pageUrls = pageUrls.filter((value) => filter(value));
|
|
}
|
|
if (pageUrls.length === 0) {
|
|
logger.warn(`No pages found!
|
|
\`${outFile}\` not created.`);
|
|
return;
|
|
}
|
|
let urlData = generateSitemap(pageUrls, finalSiteUrl.href, opts);
|
|
if (serialize) {
|
|
try {
|
|
const serializedUrls = [];
|
|
for (const item of urlData) {
|
|
const serialized = await Promise.resolve(serialize(item));
|
|
if (serialized) {
|
|
serializedUrls.push(serialized);
|
|
}
|
|
}
|
|
if (serializedUrls.length === 0) {
|
|
logger.warn("No pages found!");
|
|
return;
|
|
}
|
|
urlData = serializedUrls;
|
|
} catch (err) {
|
|
logger.error(`Error serializing pages
|
|
${err.toString()}`);
|
|
return;
|
|
}
|
|
}
|
|
const destDir = fileURLToPath(dir);
|
|
const lastmod = opts.lastmod?.toISOString();
|
|
const xslURL = opts.xslURL ? new URL(opts.xslURL, finalSiteUrl).href : void 0;
|
|
if (chunks) {
|
|
try {
|
|
let groupedUrlCollection = [];
|
|
const chunksItem = {};
|
|
for (const [key, cb] of Object.entries(chunks)) {
|
|
const collection = [];
|
|
for (const item of urlData) {
|
|
const collect = await Promise.resolve(cb(item));
|
|
if (collect) {
|
|
collection.push(collect);
|
|
}
|
|
}
|
|
chunksItem[key] = collection;
|
|
groupedUrlCollection = [
|
|
...groupedUrlCollection,
|
|
...collection.map((coll) => coll.url)
|
|
];
|
|
}
|
|
chunksItem["pages"] = urlData.filter(
|
|
(urlDataItem) => !groupedUrlCollection.includes(urlDataItem.url)
|
|
);
|
|
await writeSitemapChunk(
|
|
{
|
|
filenameBase,
|
|
hostname: finalSiteUrl.href,
|
|
sitemapHostname: finalSiteUrl.href,
|
|
sourceData: chunksItem,
|
|
destinationDir: destDir,
|
|
publicBasePath: config.base,
|
|
customSitemaps,
|
|
limit: entryLimit,
|
|
xslURL,
|
|
lastmod,
|
|
namespaces: opts.namespaces
|
|
},
|
|
config
|
|
);
|
|
logger.info(`\`${outFile}\` created at \`${path.relative(process.cwd(), destDir)}\``);
|
|
return;
|
|
} catch (err) {
|
|
logger.error(`Error chunking sitemaps
|
|
${err.toString()}`);
|
|
return;
|
|
}
|
|
}
|
|
await writeSitemap(
|
|
{
|
|
filenameBase,
|
|
hostname: finalSiteUrl.href,
|
|
destinationDir: destDir,
|
|
publicBasePath: config.base,
|
|
sourceData: urlData,
|
|
limit: entryLimit,
|
|
customSitemaps,
|
|
xslURL,
|
|
lastmod,
|
|
namespaces: opts.namespaces
|
|
},
|
|
config
|
|
);
|
|
logger.info(`\`${outFile}\` created at \`${path.relative(process.cwd(), destDir)}\``);
|
|
} catch (err) {
|
|
if (err instanceof ZodError) {
|
|
logger.warn(formatConfigErrorMessage(err));
|
|
} else {
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
var index_default = createPlugin;
|
|
export {
|
|
EnumChangefreq as ChangeFreqEnum,
|
|
index_default as default
|
|
};
|