refactor: better typings (#3634)

This commit is contained in:
Oleksii Tymoshenko 2022-06-20 22:29:53 +03:00 committed by GitHub
parent d9f6dcf6ea
commit facfc4682f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 67 deletions

View file

@ -141,7 +141,7 @@ export default {
### entryLimit ### 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__ __astro.config.mjs__
@ -166,7 +166,7 @@ export default {
`lastmod` - The date of page last modification. `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). 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. 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. Optional `links` property contains the `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). 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. The example below shows the ability to add the sitemap specific properties individually.

View file

@ -1,9 +0,0 @@
export const changefreqValues = [
'always',
'hourly',
'daily',
'weekly',
'monthly',
'yearly',
'never',
] as const;

View file

@ -1,19 +1,16 @@
import { SitemapItemLoose } from 'sitemap'; import type { SitemapOptions, SitemapItem } from './index';
import type { SitemapOptions } from './index';
import { parseUrl } from './utils/parse-url'; import { parseUrl } from './utils/parse-url';
const STATUS_CODE_PAGE_REGEXP = /\/[0-9]{3}\/?$/; const STATUS_CODE_PAGE_REGEXP = /\/[0-9]{3}\/?$/;
/** Construct sitemap.xml given a set of URLs */ /** Construct sitemap.xml given a set of URLs */
export function generateSitemap(pages: string[], finalSiteUrl: string, opts: SitemapOptions) { 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 <link rel="canonical"> URLs here // TODO: find way to respect <link rel="canonical"> URLs here
const urls = [...pages].filter((url) => !STATUS_CODE_PAGE_REGEXP.test(url)); 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 urls.sort((a, b) => a.localeCompare(b, 'en', { numeric: true })); // sort alphabetically so sitemap is same each time
const lastmod = lastmodSrc?.toISOString(); const lastmod = lastmodSrc?.toISOString();
const priority = typeof prioritySrc === 'number' ? prioritySrc : undefined;
const { locales, defaultLocale } = i18n || {}; const { locales, defaultLocale } = i18n || {};
const localeCodes = Object.keys(locales || {}); const localeCodes = Object.keys(locales || {});
@ -27,7 +24,7 @@ export function generateSitemap(pages: string[], finalSiteUrl: string, opts: Sit
return result?.locale; return result?.locale;
}; };
const urlData = urls.map((url) => { const urlData: SitemapItem[] = urls.map((url) => {
let links; let links;
if (defaultLocale && locales) { if (defaultLocale && locales) {
const currentPath = getPath(url); const currentPath = getPath(url);
@ -47,8 +44,8 @@ export function generateSitemap(pages: string[], finalSiteUrl: string, opts: Sit
links, links,
lastmod, lastmod,
priority, priority,
changefreq, // : changefreq as EnumChangefreq, changefreq,
} as SitemapItemLoose; };
}); });
return urlData; return urlData;

View file

@ -1,14 +1,13 @@
import type { AstroConfig, AstroIntegration } from 'astro'; 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 { fileURLToPath } from 'url';
import { ZodError } from 'zod'; import { ZodError } from 'zod';
import { changefreqValues } from './constants';
import { generateSitemap } from './generate-sitemap'; import { generateSitemap } from './generate-sitemap';
import { Logger } from './utils/logger'; import { Logger } from './utils/logger';
import { validateOptions } from './validate-options'; import { validateOptions } from './validate-options';
export type ChangeFreq = typeof changefreqValues[number]; export type ChangeFreq = EnumChangefreq;
export type SitemapItem = Pick< export type SitemapItem = Pick<
SitemapItemLoose, SitemapItemLoose,
'url' | 'lastmod' | 'changefreq' | 'priority' | 'links' 'url' | 'lastmod' | 'changefreq' | 'priority' | 'links'
@ -34,7 +33,7 @@ export type SitemapOptions =
priority?: number; priority?: number;
// called for each sitemap item just before to save them on disk, sync or async // called for each sitemap item just before to save them on disk, sync or async
serialize?(item: SitemapItemLoose): SitemapItemLoose; serialize?(item: SitemapItem): SitemapItem | Promise<SitemapItem>;
} }
| undefined; | undefined;
@ -103,7 +102,7 @@ const createPlugin = (options?: SitemapOptions): AstroIntegration => {
if (serialize) { if (serialize) {
try { try {
const serializedUrls: SitemapItemLoose[] = []; const serializedUrls: SitemapItem[] = [];
for (const item of urlData) { for (const item of urlData) {
const serialized = await Promise.resolve(serialize(item)); const serialized = await Promise.resolve(serialize(item));
serializedUrls.push(serialized); serializedUrls.push(serialized);

View file

@ -1,47 +1,39 @@
import { z } from 'zod'; import { z } from 'zod';
import { EnumChangefreq as ChangeFreq } from 'sitemap';
import { SITEMAP_CONFIG_DEFAULTS } from './config-defaults'; import { SITEMAP_CONFIG_DEFAULTS } from './config-defaults';
import { changefreqValues } from './constants';
const localeKeySchema = () => z.string().min(1); 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();
export const SitemapOptionsSchema = z export const SitemapOptionsSchema = z
.object({ .object({
filter: fnSchema(), filter: z.function().args(z.string()).returns(z.boolean()).optional(),
customPages: z.string().url().array().optional(), customPages: z.string().url().array().optional(),
canonicalURL: z.string().url().optional(), canonicalURL: z.string().url().optional(),
i18n: z i18n: z
.object({ .object({
defaultLocale: localeKeySchema(), defaultLocale: localeKeySchema,
locales: z.record( locales: z.record(
localeKeySchema(), localeKeySchema,
z z
.string() .string()
.min(2) .min(2)
.regex(/^[a-zA-Z\-]+$/gm, { .regex(/^[a-zA-Z\-]+$/gm, {
message: 'Only English alphabet symbols and hyphen allowed', message: 'Only English alphabet symbols and hyphen allowed',
}) }),
), ),
}) })
.refine((val) => !val || val.locales[val.defaultLocale], { .refine((val) => !val || val.locales[val.defaultLocale], {
message: '`defaultLocale` must exists in `locales` keys', message: '`defaultLocale` must exist in `locales` keys',
}) })
.optional(), .optional(),
entryLimit: z.number().nonnegative().default(SITEMAP_CONFIG_DEFAULTS.entryLimit), entryLimit: z.number().nonnegative().optional().default(SITEMAP_CONFIG_DEFAULTS.entryLimit),
serialize: fnSchema(), serialize: z.function().args(z.any()).returns(z.any()).optional(),
changefreq: z.enum(changefreqValues).optional(), changefreq: z.nativeEnum(ChangeFreq).optional(),
lastmod: z.date().optional(), lastmod: z.date().optional(),
priority: z.number().min(0).max(1).optional(), priority: z.number().min(0).max(1).optional(),
}) })
.strict() .strict()
.default(SITEMAP_CONFIG_DEFAULTS); .default(SITEMAP_CONFIG_DEFAULTS);