aboutsummaryrefslogtreecommitdiff
path: root/packages/astro-rss/src/util.ts
diff options
context:
space:
mode:
authorGravatar github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> 2025-06-05 14:25:23 +0000
committerGravatar github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> 2025-06-05 14:25:23 +0000
commite586d7d704d475afe3373a1de6ae20d504f79d6d (patch)
tree7e3fa24807cebd48a86bd40f866d792181191ee9 /packages/astro-rss/src/util.ts
downloadastro-e586d7d704d475afe3373a1de6ae20d504f79d6d.tar.gz
astro-e586d7d704d475afe3373a1de6ae20d504f79d6d.tar.zst
astro-e586d7d704d475afe3373a1de6ae20d504f79d6d.zip
Sync from a8e1c0a7402940e0fc5beef669522b315052df1blatest
Diffstat (limited to 'packages/astro-rss/src/util.ts')
-rw-r--r--packages/astro-rss/src/util.ts53
1 files changed, 53 insertions, 0 deletions
diff --git a/packages/astro-rss/src/util.ts b/packages/astro-rss/src/util.ts
new file mode 100644
index 000000000..43de370dd
--- /dev/null
+++ b/packages/astro-rss/src/util.ts
@@ -0,0 +1,53 @@
+import type { z } from 'astro/zod';
+import type { RSSOptions } from './index.js';
+
+/** Normalize URL to its canonical form */
+export function createCanonicalURL(
+ url: string,
+ trailingSlash?: RSSOptions['trailingSlash'],
+ base?: string,
+): string {
+ let pathname = url.replace(/\/index.html$/, ''); // index.html is not canonical
+ if (!getUrlExtension(url)) {
+ // add trailing slash if there’s no extension or `trailingSlash` is true
+ pathname = pathname.replace(/\/*$/, '/');
+ }
+
+ pathname = pathname.replace(/\/+/g, '/'); // remove duplicate slashes (URL() won’t)
+
+ const canonicalUrl = new URL(pathname, base).href;
+ if (trailingSlash === false) {
+ // remove the trailing slash
+ return canonicalUrl.replace(/\/*$/, '');
+ }
+ return canonicalUrl;
+}
+
+/** Check if a URL is already valid */
+export function isValidURL(url: string): boolean {
+ try {
+ new URL(url);
+ return true;
+ } catch {}
+ return false;
+}
+
+function getUrlExtension(url: string) {
+ const lastDot = url.lastIndexOf('.');
+ const lastSlash = url.lastIndexOf('/');
+ return lastDot > lastSlash ? url.slice(lastDot + 1) : '';
+}
+
+const flattenErrorPath = (errorPath: (string | number)[]) => errorPath.join('.');
+
+export const errorMap: z.ZodErrorMap = (error, ctx) => {
+ if (error.code === 'invalid_type') {
+ const badKeyPath = JSON.stringify(flattenErrorPath(error.path));
+ if (error.received === 'undefined') {
+ return { message: `${badKeyPath} is required.` };
+ } else {
+ return { message: `${badKeyPath} should be ${error.expected}, not ${error.received}.` };
+ }
+ }
+ return { message: ctx.defaultError };
+};