refactor: better typings (#3634)
This commit is contained in:
parent
d9f6dcf6ea
commit
facfc4682f
5 changed files with 46 additions and 67 deletions
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
export const changefreqValues = [
|
|
||||||
'always',
|
|
||||||
'hourly',
|
|
||||||
'daily',
|
|
||||||
'weekly',
|
|
||||||
'monthly',
|
|
||||||
'yearly',
|
|
||||||
'never',
|
|
||||||
] as const;
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue