From facfc4682fd6576378b7e1deb5b5babc3307bf58 Mon Sep 17 00:00:00 2001 From: Oleksii Tymoshenko Date: Mon, 20 Jun 2022 22:29:53 +0300 Subject: [PATCH] refactor: better typings (#3634) --- packages/integrations/sitemap/README.md | 12 ++-- .../integrations/sitemap/src/constants.ts | 9 --- .../sitemap/src/generate-sitemap.ts | 13 ++-- packages/integrations/sitemap/src/index.ts | 9 ++- packages/integrations/sitemap/src/schema.ts | 70 ++++++++----------- 5 files changed, 46 insertions(+), 67 deletions(-) delete mode 100644 packages/integrations/sitemap/src/constants.ts diff --git a/packages/integrations/sitemap/README.md b/packages/integrations/sitemap/README.md index ab880a075..fda6577e8 100644 --- a/packages/integrations/sitemap/README.md +++ b/packages/integrations/sitemap/README.md @@ -141,7 +141,7 @@ export default { ### entryLimit -Non-negative `Number` of entries per sitemap file. Default value is 45000. A sitemap index and multiple sitemaps are created if you have more entries. See explanation on [Google](https://developers.google.com/search/docs/advanced/sitemaps/large-sitemaps). +Non-negative `Number` of entries per sitemap file. Default value is 45000. A sitemap index and multiple sitemaps are created if you have more entries. See explanation about large sitemaps on [Google](https://developers.google.com/search/docs/advanced/sitemaps/large-sitemaps). __astro.config.mjs__ @@ -166,7 +166,7 @@ export default { `lastmod` - The date of page last modification. -`changefreq` and `priority` are ignored by Google. +The `changefreq` and `priority` are ignored by Google. See detailed explanation of sitemap specific options on [sitemap.org](https://www.sitemaps.org/protocol.html). @@ -194,12 +194,12 @@ export default { Async or sync function called for each sitemap entry just before writing to a disk. -It receives as parameter `SitemapItem` object which consists of `url` (required, absolute page URL) and optional `changefreq`, `lastmod`, `priority` and `links` properties. +It receives as parameter a `SitemapItem` object which consists of `url` (required, absolute page URL) and optional `changefreq`, `lastmod` (ISO formatted date, `String` type), `priority` and `links` properties. -Optional `links` property contains a `LinkItem` list of alternate pages including a parent page. -`LinkItem` type has two required fields: `url` (the fully-qualified URL for the version of this page for the specified language) and `hreflang` (a supported language code targeted by this version of the page). +Optional `links` property contains the `LinkItem` list of alternate pages including a parent page. +The `LinkItem` type has two required fields: `url` (the fully-qualified URL for the version of this page for the specified language) and `lang` (a supported language code targeted by this version of the page). -`serialize` function should return `SitemapItem`, touched or not. +The `serialize` function should return `SitemapItem`, touched or not. The example below shows the ability to add the sitemap specific properties individually. diff --git a/packages/integrations/sitemap/src/constants.ts b/packages/integrations/sitemap/src/constants.ts deleted file mode 100644 index 431cc5954..000000000 --- a/packages/integrations/sitemap/src/constants.ts +++ /dev/null @@ -1,9 +0,0 @@ -export const changefreqValues = [ - 'always', - 'hourly', - 'daily', - 'weekly', - 'monthly', - 'yearly', - 'never', -] as const; diff --git a/packages/integrations/sitemap/src/generate-sitemap.ts b/packages/integrations/sitemap/src/generate-sitemap.ts index 3c39e1f7e..0a5318c31 100644 --- a/packages/integrations/sitemap/src/generate-sitemap.ts +++ b/packages/integrations/sitemap/src/generate-sitemap.ts @@ -1,19 +1,16 @@ -import { SitemapItemLoose } from 'sitemap'; - -import type { SitemapOptions } from './index'; +import type { SitemapOptions, SitemapItem } from './index'; import { parseUrl } from './utils/parse-url'; const STATUS_CODE_PAGE_REGEXP = /\/[0-9]{3}\/?$/; /** Construct sitemap.xml given a set of URLs */ export function generateSitemap(pages: string[], finalSiteUrl: string, opts: SitemapOptions) { - const { changefreq, priority: prioritySrc, lastmod: lastmodSrc, i18n } = opts || {}; + const { changefreq, priority, lastmod: lastmodSrc, i18n } = opts!; // TODO: find way to respect URLs here const urls = [...pages].filter((url) => !STATUS_CODE_PAGE_REGEXP.test(url)); urls.sort((a, b) => a.localeCompare(b, 'en', { numeric: true })); // sort alphabetically so sitemap is same each time const lastmod = lastmodSrc?.toISOString(); - const priority = typeof prioritySrc === 'number' ? prioritySrc : undefined; const { locales, defaultLocale } = i18n || {}; const localeCodes = Object.keys(locales || {}); @@ -27,7 +24,7 @@ export function generateSitemap(pages: string[], finalSiteUrl: string, opts: Sit return result?.locale; }; - const urlData = urls.map((url) => { + const urlData: SitemapItem[] = urls.map((url) => { let links; if (defaultLocale && locales) { const currentPath = getPath(url); @@ -47,8 +44,8 @@ export function generateSitemap(pages: string[], finalSiteUrl: string, opts: Sit links, lastmod, priority, - changefreq, // : changefreq as EnumChangefreq, - } as SitemapItemLoose; + changefreq, + }; }); return urlData; diff --git a/packages/integrations/sitemap/src/index.ts b/packages/integrations/sitemap/src/index.ts index d35d635ca..d0888d302 100644 --- a/packages/integrations/sitemap/src/index.ts +++ b/packages/integrations/sitemap/src/index.ts @@ -1,14 +1,13 @@ import type { AstroConfig, AstroIntegration } from 'astro'; -import { LinkItem as LinkItemBase, simpleSitemapAndIndex, SitemapItemLoose } from 'sitemap'; +import { LinkItem as LinkItemBase, simpleSitemapAndIndex, SitemapItemLoose, EnumChangefreq } from 'sitemap'; import { fileURLToPath } from 'url'; import { ZodError } from 'zod'; -import { changefreqValues } from './constants'; import { generateSitemap } from './generate-sitemap'; import { Logger } from './utils/logger'; import { validateOptions } from './validate-options'; -export type ChangeFreq = typeof changefreqValues[number]; +export type ChangeFreq = EnumChangefreq; export type SitemapItem = Pick< SitemapItemLoose, 'url' | 'lastmod' | 'changefreq' | 'priority' | 'links' @@ -34,7 +33,7 @@ export type SitemapOptions = priority?: number; // called for each sitemap item just before to save them on disk, sync or async - serialize?(item: SitemapItemLoose): SitemapItemLoose; + serialize?(item: SitemapItem): SitemapItem | Promise; } | undefined; @@ -103,7 +102,7 @@ const createPlugin = (options?: SitemapOptions): AstroIntegration => { if (serialize) { try { - const serializedUrls: SitemapItemLoose[] = []; + const serializedUrls: SitemapItem[] = []; for (const item of urlData) { const serialized = await Promise.resolve(serialize(item)); serializedUrls.push(serialized); diff --git a/packages/integrations/sitemap/src/schema.ts b/packages/integrations/sitemap/src/schema.ts index e8c198ced..3175a2e28 100644 --- a/packages/integrations/sitemap/src/schema.ts +++ b/packages/integrations/sitemap/src/schema.ts @@ -1,47 +1,39 @@ import { z } from 'zod'; +import { EnumChangefreq as ChangeFreq } from 'sitemap'; import { SITEMAP_CONFIG_DEFAULTS } from './config-defaults'; -import { changefreqValues } from './constants'; -const localeKeySchema = () => z.string().min(1); - -const isFunction = (fn: any) => fn instanceof Function; - -const fnSchema = () => - z - .any() - .refine((val) => !val || isFunction(val), { message: 'Not a function' }) - .optional(); +const localeKeySchema = z.string().min(1); export const SitemapOptionsSchema = z - .object({ - filter: fnSchema(), - customPages: z.string().url().array().optional(), - canonicalURL: z.string().url().optional(), + .object({ + filter: z.function().args(z.string()).returns(z.boolean()).optional(), + customPages: z.string().url().array().optional(), + canonicalURL: z.string().url().optional(), - i18n: z - .object({ - defaultLocale: localeKeySchema(), - locales: z.record( - localeKeySchema(), - z - .string() - .min(2) - .regex(/^[a-zA-Z\-]+$/gm, { - message: 'Only English alphabet symbols and hyphen allowed', - }) - ), - }) - .refine((val) => !val || val.locales[val.defaultLocale], { - message: '`defaultLocale` must exists in `locales` keys', - }) - .optional(), + i18n: z + .object({ + defaultLocale: localeKeySchema, + locales: z.record( + localeKeySchema, + z + .string() + .min(2) + .regex(/^[a-zA-Z\-]+$/gm, { + message: 'Only English alphabet symbols and hyphen allowed', + }), + ), + }) + .refine((val) => !val || val.locales[val.defaultLocale], { + message: '`defaultLocale` must exist in `locales` keys', + }) + .optional(), - entryLimit: z.number().nonnegative().default(SITEMAP_CONFIG_DEFAULTS.entryLimit), - serialize: fnSchema(), + entryLimit: z.number().nonnegative().optional().default(SITEMAP_CONFIG_DEFAULTS.entryLimit), + serialize: z.function().args(z.any()).returns(z.any()).optional(), - changefreq: z.enum(changefreqValues).optional(), - lastmod: z.date().optional(), - priority: z.number().min(0).max(1).optional(), - }) - .strict() - .default(SITEMAP_CONFIG_DEFAULTS); + changefreq: z.nativeEnum(ChangeFreq).optional(), + lastmod: z.date().optional(), + priority: z.number().min(0).max(1).optional(), + }) + .strict() + .default(SITEMAP_CONFIG_DEFAULTS);