diff --git a/packages/integrations/sitemap/README.md b/packages/integrations/sitemap/README.md
index 601598a01..ab880a075 100644
--- a/packages/integrations/sitemap/README.md
+++ b/packages/integrations/sitemap/README.md
@@ -94,12 +94,6 @@ Generated sitemap content for two pages website:
```
-All pages generated during build will contain in `
` section a link to sitemap:
-
-```html
-
-```
-
You can also check our [Astro Integration Documentation][astro-integration] for more on integrations.
## Configuration
@@ -164,26 +158,6 @@ export default {
}
```
-### createLinkInHead
-
-`Boolean`, default is `true`, create a link on sitemap in `` section of generated pages.
-
-__astro.config.mjs__
-
-```js
-import sitemap from '@astrojs/sitemap';
-
-export default {
- site: 'https://stargazers.club',
- integrations: [
- sitemap({
- // disable create links to sitemap in
- createLinkInHead: false,
- }),
- ],
-}
-```
-
### changefreq, lastmod, priority
`changefreq` - How frequently the page is likely to change. Available values: `always` \| `hourly` \| `daily` \| `weekly` \| `monthly` \| `yearly` \| `never`.
@@ -197,7 +171,7 @@ export default {
See detailed explanation of sitemap specific options on [sitemap.org](https://www.sitemaps.org/protocol.html).
-:exclamation: This integration uses 'astro:build:done' hook. The hook exposes only generated page paths. So with present version of Astro the integration has no abilities to analyze a page source, frontmatter etc. The integration can add `changefreq`, `lastmod` and `priority` attributes only in a batch or nothing.
+:exclamation: This integration uses 'astro:build:done' hook. The hook exposes generated page paths only. So with present version of Astro the integration has no abilities to analyze a page source, frontmatter etc. The integration can add `changefreq`, `lastmod` and `priority` attributes only in a batch or nothing.
__astro.config.mjs__
@@ -210,7 +184,7 @@ export default {
sitemap({
changefreq: 'weekly',
priority: 0.7,
- lastmod: new Date('2022-05-28'),
+ lastmod: new Date('2022-02-24'),
}),
],
}
@@ -218,15 +192,17 @@ export default {
### serialize
-Async or sync function called for each sitemap entry just before writing to 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 URL of page) and optional `changefreq`, `lastmod`, `priority` and `links` properties.
+It receives as parameter `SitemapItem` object which consists of `url` (required, absolute page URL) and optional `changefreq`, `lastmod`, `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).
`serialize` function should return `SitemapItem`, touched or not.
+The example below shows the ability to add the sitemap specific properties individually.
+
__astro.config.mjs__
```js
@@ -237,7 +213,7 @@ export default {
integrations: [
sitemap({
serialize(item) {
- if (/special-page/.test(item.url)) {
+ if (/your-special-page/.test(item.url)) {
item.changefreq = 'daily';
item.lastmod = new Date();
item.priority = 0.9;
@@ -251,7 +227,7 @@ export default {
### i18n
-To localize sitemap you should supply the integration config with the `i18n` option. The integration will check generated page paths on presence of locale keys in paths.
+To localize a sitemap you should supply the integration config with the `i18n` option. The integration will check generated page paths on presence of locale keys in paths.
`i18n` object has two required properties:
diff --git a/packages/integrations/sitemap/package.json b/packages/integrations/sitemap/package.json
index 12b49ec4b..d61c608b3 100644
--- a/packages/integrations/sitemap/package.json
+++ b/packages/integrations/sitemap/package.json
@@ -31,9 +31,8 @@
"dev": "astro-scripts dev \"src/**/*.ts\""
},
"dependencies": {
- "node-html-parser": "^5.3.3",
"sitemap": "^7.1.1",
- "zod": "^3.17.3"
+ "zod": "^3.17.3"
},
"devDependencies": {
"astro": "workspace:*",
diff --git a/packages/integrations/sitemap/src/config-defaults.ts b/packages/integrations/sitemap/src/config-defaults.ts
new file mode 100644
index 000000000..22288fc11
--- /dev/null
+++ b/packages/integrations/sitemap/src/config-defaults.ts
@@ -0,0 +1,5 @@
+import type { SitemapOptions } from './index';
+
+export const SITEMAP_CONFIG_DEFAULTS: SitemapOptions & any = {
+ entryLimit: 45000,
+};
diff --git a/packages/integrations/sitemap/src/constants.ts b/packages/integrations/sitemap/src/constants.ts
index c983e1b1c..431cc5954 100644
--- a/packages/integrations/sitemap/src/constants.ts
+++ b/packages/integrations/sitemap/src/constants.ts
@@ -1 +1,9 @@
-export const changefreqValues = ['always', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'never'] as const;
+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 f2c2ea7ce..3c39e1f7e 100644
--- a/packages/integrations/sitemap/src/generate-sitemap.ts
+++ b/packages/integrations/sitemap/src/generate-sitemap.ts
@@ -7,49 +7,49 @@ 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 || {};
- // 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 { changefreq, priority: prioritySrc, 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 lastmod = lastmodSrc?.toISOString();
+ const priority = typeof prioritySrc === 'number' ? prioritySrc : undefined;
- const { locales, defaultLocale } = i18n || {};
- const localeCodes = Object.keys(locales || {});
+ const { locales, defaultLocale } = i18n || {};
+ const localeCodes = Object.keys(locales || {});
- const getPath = (url: string) => {
- const result = parseUrl(url, i18n?.defaultLocale || '', localeCodes, finalSiteUrl);
- return result?.path;
- };
- const getLocale = (url: string) => {
- const result = parseUrl(url, i18n?.defaultLocale || '', localeCodes, finalSiteUrl);
- return result?.locale;
- };
+ const getPath = (url: string) => {
+ const result = parseUrl(url, i18n?.defaultLocale || '', localeCodes, finalSiteUrl);
+ return result?.path;
+ };
+ const getLocale = (url: string) => {
+ const result = parseUrl(url, i18n?.defaultLocale || '', localeCodes, finalSiteUrl);
+ return result?.locale;
+ };
- const urlData = urls.map((url) => {
- let links;
- if (defaultLocale && locales) {
- const currentPath = getPath(url);
- if (currentPath) {
- const filtered = urls.filter((subUrl) => getPath(subUrl) === currentPath);
- if (filtered.length > 1) {
- links = filtered.map((subUrl) => ({
- url: subUrl,
- lang: locales[getLocale(subUrl)!],
- }));
- }
- }
- }
+ const urlData = urls.map((url) => {
+ let links;
+ if (defaultLocale && locales) {
+ const currentPath = getPath(url);
+ if (currentPath) {
+ const filtered = urls.filter((subUrl) => getPath(subUrl) === currentPath);
+ if (filtered.length > 1) {
+ links = filtered.map((subUrl) => ({
+ url: subUrl,
+ lang: locales[getLocale(subUrl)!],
+ }));
+ }
+ }
+ }
- return {
- url,
- links,
- lastmod,
- priority,
- changefreq, // : changefreq as EnumChangefreq,
- } as SitemapItemLoose;
- });
+ return {
+ url,
+ links,
+ lastmod,
+ priority,
+ changefreq, // : changefreq as EnumChangefreq,
+ } as SitemapItemLoose;
+ });
- return urlData;
+ return urlData;
}
diff --git a/packages/integrations/sitemap/src/index.ts b/packages/integrations/sitemap/src/index.ts
index d0004ae91..700f68876 100644
--- a/packages/integrations/sitemap/src/index.ts
+++ b/packages/integrations/sitemap/src/index.ts
@@ -1,148 +1,138 @@
-import path from 'node:path';
import { fileURLToPath } from 'url';
import type { AstroConfig, AstroIntegration } from 'astro';
import { ZodError } from 'zod';
import { LinkItem as LinkItemBase, SitemapItemLoose, simpleSitemapAndIndex } from 'sitemap';
import { Logger } from './utils/logger';
-import { withOptions } from './with-options';
-import { validateOpts } from './validate-opts';
-import { generateSitemap } from './generate-sitemap';
import { changefreqValues } from './constants';
-import { processPages } from './process-pages';
+import { validateOptions } from './validate-options';
+import { generateSitemap } from './generate-sitemap';
export type ChangeFreq = typeof changefreqValues[number];
-export type SitemapItem = Pick;
+export type SitemapItem = Pick<
+ SitemapItemLoose,
+ 'url' | 'lastmod' | 'changefreq' | 'priority' | 'links'
+>;
export type LinkItem = LinkItemBase;
export type SitemapOptions =
- | {
- // the same with official
- filter?(page: string): boolean;
- customPages?: string[];
- canonicalURL?: string;
- // added
- i18n?: {
- defaultLocale: string;
- locales: Record;
- };
- entryLimit?: number;
+ | {
+ filter?(page: string): boolean;
+ customPages?: string[];
+ canonicalURL?: string;
- createLinkInHead?: boolean;
- serialize?(item: SitemapItemLoose): SitemapItemLoose;
- // sitemap specific
- changefreq?: ChangeFreq;
- lastmod?: Date;
- priority?: number;
- }
- | undefined;
+ i18n?: {
+ defaultLocale: string;
+ locales: Record;
+ };
+ // number of entries per sitemap file
+ entryLimit?: number;
+
+ // sitemap specific
+ changefreq?: ChangeFreq;
+ lastmod?: Date;
+ priority?: number;
+
+ // called for each sitemap item just before to save them on disk, sync or async
+ serialize?(item: SitemapItemLoose): SitemapItemLoose;
+ }
+ | undefined;
function formatConfigErrorMessage(err: ZodError) {
- const errorList = err.issues.map((issue) => ` ${issue.path.join('.')} ${issue.message + '.'}`);
- return errorList.join('\n');
+ const errorList = err.issues.map((issue) => ` ${issue.path.join('.')} ${issue.message + '.'}`);
+ return errorList.join('\n');
}
const PKG_NAME = '@astrojs/sitemap';
const OUTFILE = 'sitemap-index.xml';
const createPlugin = (options?: SitemapOptions): AstroIntegration => {
- let config: AstroConfig;
- return {
- name: PKG_NAME,
+ let config: AstroConfig;
+ return {
+ name: PKG_NAME,
- hooks: {
- 'astro:config:done': async ({ config: cfg }) => {
- config = cfg;
- },
+ hooks: {
+ 'astro:config:done': async ({ config: cfg }) => {
+ config = cfg;
+ },
- 'astro:build:done': async ({ dir, pages }) => {
- const logger = new Logger(PKG_NAME);
+ 'astro:build:done': async ({ dir, pages }) => {
+ const logger = new Logger(PKG_NAME);
- const opts = withOptions(options || {});
+ try {
+ const opts = validateOptions(config.site, options);
- try {
- validateOpts(config.site, opts);
+ const { filter, customPages, canonicalURL, serialize, entryLimit } = opts;
- const { filter, customPages, canonicalURL, serialize, createLinkInHead, entryLimit } = opts;
+ let finalSiteUrl: URL;
+ if (canonicalURL) {
+ finalSiteUrl = new URL(canonicalURL);
+ if (!finalSiteUrl.pathname.endsWith('/')) {
+ finalSiteUrl.pathname += '/'; // normalizes the final url since it's provided by user
+ }
+ } else {
+ // `validateOptions` forces to provide `canonicalURL` or `config.site` at least.
+ // So step to check on empty values of `canonicalURL` and `config.site` is dropped.
+ finalSiteUrl = new URL(config.base, config.site);
+ }
- let finalSiteUrl: URL;
- if (canonicalURL) {
- finalSiteUrl = new URL(canonicalURL);
- if (!finalSiteUrl.pathname.endsWith('/')) {
- finalSiteUrl.pathname += '/'; // normalizes the final url since it's provided by user
- }
- } else {
- // `validateOpts` forces to provide `canonicalURL` or `config.site` at least.
- // So step to check on empty values of `canonicalURL` and `config.site` is dropped.
- finalSiteUrl = new URL(config.base, config.site);
- }
+ let pageUrls = pages.map((p) => {
+ const path = finalSiteUrl.pathname + p.pathname;
+ return new URL(path, finalSiteUrl).href;
+ });
- let pageUrls = pages.map((p) => {
- const path = finalSiteUrl.pathname + p.pathname;
- return new URL(path, finalSiteUrl).href;
- });
+ try {
+ if (filter) {
+ pageUrls = pageUrls.filter(filter);
+ }
+ } catch (err) {
+ logger.error(`Error filtering pages\n${(err as any).toString()}`);
+ return;
+ }
- try {
- if (filter) {
- pageUrls = pageUrls.filter((url) => filter(url));
- }
- } catch (err) {
- logger.error(`Error filtering pages\n${(err as any).toString()}`);
- return;
- }
+ if (customPages) {
+ pageUrls = [...pageUrls, ...customPages];
+ }
- if (customPages) {
- pageUrls = [...pageUrls, ...customPages];
- }
+ if (pageUrls.length === 0) {
+ logger.warn(`No data for sitemap.\n\`${OUTFILE}\` is not created.`);
+ return;
+ }
- if (pageUrls.length === 0) {
- logger.warn(`No data for sitemap.\n\`${OUTFILE}\` is not created.`);
- return;
- }
+ let urlData = generateSitemap(pageUrls, finalSiteUrl.href, opts);
- let urlData = generateSitemap(pageUrls, finalSiteUrl.href, opts);
+ if (serialize) {
+ try {
+ const serializedUrls: SitemapItemLoose[] = [];
+ for (const item of urlData) {
+ const serialized = await Promise.resolve(serialize(item));
+ serializedUrls.push(serialized);
+ }
+ urlData = serializedUrls;
+ } catch (err) {
+ logger.error(`Error serializing pages\n${(err as any).toString()}`);
+ return;
+ }
+ }
- let serializedUrls: SitemapItemLoose[];
-
- if (serialize) {
- serializedUrls = [];
- try {
- for (const item of urlData) {
- const serialized = await Promise.resolve(serialize(item));
- serializedUrls.push(serialized);
- }
- urlData = serializedUrls;
- } catch (err) {
- logger.error(`Error serializing pages\n${(err as any).toString()}`);
- return;
- }
- }
-
- await simpleSitemapAndIndex({
- hostname: finalSiteUrl.href,
- destinationDir: fileURLToPath(dir),
- sourceData: urlData,
- limit: entryLimit,
- gzip: false,
- });
- logger.success(`\`${OUTFILE}\` is created.`);
-
- if (createLinkInHead) {
- const sitemapHref = path.posix.join(config.base, OUTFILE);
- const headHTML = ``;
- await processPages(pages, dir, headHTML, config.build.format);
- logger.success('Sitemap links are created in section of generated pages.');
- }
- } catch (err) {
- if (err instanceof ZodError) {
- logger.warn(formatConfigErrorMessage(err));
- } else {
- throw err;
- }
- }
- },
- },
- };
+ await simpleSitemapAndIndex({
+ hostname: finalSiteUrl.href,
+ destinationDir: fileURLToPath(dir),
+ sourceData: urlData,
+ limit: entryLimit,
+ gzip: false,
+ });
+ logger.success(`\`${OUTFILE}\` is created.`);
+ } catch (err) {
+ if (err instanceof ZodError) {
+ logger.warn(formatConfigErrorMessage(err));
+ } else {
+ throw err;
+ }
+ }
+ },
+ },
+ };
};
export default createPlugin;
diff --git a/packages/integrations/sitemap/src/process-pages.ts b/packages/integrations/sitemap/src/process-pages.ts
deleted file mode 100644
index c6a46218c..000000000
--- a/packages/integrations/sitemap/src/process-pages.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { promises as fs } from 'node:fs';
-import { parse, HTMLElement } from 'node-html-parser';
-
-const addTailSlash = (s: string) => (s.endsWith('/') ? s : s + '/');
-const removeHeadingSlash = (s: string) => s.replace(/^\/+/, '');
-const removeTrailingSlash = (s: string) => s.replace(/\/+$/, '');
-
-const getFileDir = (pathname: string) => {
- const name = addTailSlash(pathname);
- const file = name === '404/' ? '404.html' : `${name}index.html`;
- return removeHeadingSlash(file);
-};
-
-const getFileFile = (pathname: string) => (pathname ? `${removeTrailingSlash(pathname)}.html` : 'index.html');
-
-export async function processPages(pages: { pathname: string }[], dir: URL, headHTML: string, buildFormat: string) {
- if (pages.length === 0) {
- return;
- }
- if (buildFormat !== 'directory' && buildFormat !== 'file') {
- throw new Error(`Unsupported build.format: '${buildFormat}' in your astro.config`);
- }
-
- for (const page of pages) {
- const fileUrl = new URL(buildFormat === 'directory' ? getFileDir(page.pathname) : getFileFile(page.pathname), dir);
-
- const html = await fs.readFile(fileUrl, 'utf-8');
- const root = parse(html);
- let head = root.querySelector('head');
- if (!head) {
- head = new HTMLElement('head', {}, '', root);
- root.appendChild(head);
- console.warn(`No found in \`${fileUrl.pathname}\`. will be created.`);
- }
- head.innerHTML = head.innerHTML + headHTML;
- const inlined = root.toString();
- await fs.writeFile(fileUrl, inlined, 'utf-8');
- }
-}
diff --git a/packages/integrations/sitemap/src/schema.ts b/packages/integrations/sitemap/src/schema.ts
index 6e58218c7..723f9ac58 100644
--- a/packages/integrations/sitemap/src/schema.ts
+++ b/packages/integrations/sitemap/src/schema.ts
@@ -1,52 +1,47 @@
import { z } from 'zod';
-import { isValidUrl } from './utils/is-valid-url';
import { changefreqValues } from './constants';
-
-const urlSchema = () =>
- z
- .string()
- .min(1)
- .refine((val) => !val || isValidUrl(val), 'Not valid url');
+import { SITEMAP_CONFIG_DEFAULTS } from './config-defaults';
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 fnSchema = () =>
+ z
+ .any()
+ .refine((val) => !val || isFunction(val), { message: 'Not a function' })
+ .optional();
-export const SitemapOptionsSchema = z.object({
- filter: fnSchema(),
+export const SitemapOptionsSchema = z
+ .object({
+ filter: fnSchema(),
+ customPages: z.string().url().array().optional(),
+ canonicalURL: z.string().url().optional(),
- customPages: urlSchema().array().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(),
- canonicalURL: urlSchema().optional(),
+ entryLimit: z.number().nonnegative().default(SITEMAP_CONFIG_DEFAULTS.entryLimit),
+ serialize: fnSchema(),
- 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(({ locales, defaultLocale }) => locales[defaultLocale], {
- message: '`defaultLocale` must exists in `locales` keys',
- })
- .optional(),
-
- createLinkInHead: z.boolean().optional(),
-
- entryLimit: z.number().nonnegative().optional(),
-
- serialize: fnSchema(),
-
- changefreq: z.enum(changefreqValues).optional(),
- lastmod: z.date().optional(),
- priority: z.number().min(0).max(1).optional(),
-});
+ changefreq: z.enum(changefreqValues).optional(),
+ lastmod: z.date().optional(),
+ priority: z.number().min(0).max(1).optional(),
+ })
+ .strict()
+ .default(SITEMAP_CONFIG_DEFAULTS);
diff --git a/packages/integrations/sitemap/src/utils/is-object-empty.ts b/packages/integrations/sitemap/src/utils/is-object-empty.ts
index 2dbc0cfe9..0d6181069 100644
--- a/packages/integrations/sitemap/src/utils/is-object-empty.ts
+++ b/packages/integrations/sitemap/src/utils/is-object-empty.ts
@@ -1,10 +1,10 @@
// @internal
export const isObjectEmpty = (o: any) => {
- if (!o) {
- return true;
- }
- if (Array.isArray(o)) {
- return o.length === 0;
- }
- return Object.keys(o).length === 0 && Object.getPrototypeOf(o) === Object.prototype;
+ if (!o) {
+ return true;
+ }
+ if (Array.isArray(o)) {
+ return o.length === 0;
+ }
+ return Object.keys(o).length === 0 && Object.getPrototypeOf(o) === Object.prototype;
};
diff --git a/packages/integrations/sitemap/src/utils/is-valid-url.ts b/packages/integrations/sitemap/src/utils/is-valid-url.ts
index 4bead70fd..b140623b0 100644
--- a/packages/integrations/sitemap/src/utils/is-valid-url.ts
+++ b/packages/integrations/sitemap/src/utils/is-valid-url.ts
@@ -1,13 +1,13 @@
// @internal
export const isValidUrl = (s: any) => {
- if (typeof s !== 'string' || !s) {
- return false;
- }
- try {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const dummy = new URL(s);
- return true;
- } catch {
- return false;
- }
+ if (typeof s !== 'string' || !s) {
+ return false;
+ }
+ try {
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ const dummy = new URL(s);
+ return true;
+ } catch {
+ return false;
+ }
};
diff --git a/packages/integrations/sitemap/src/utils/logger.ts b/packages/integrations/sitemap/src/utils/logger.ts
index ff6276a2e..203baeaa7 100644
--- a/packages/integrations/sitemap/src/utils/logger.ts
+++ b/packages/integrations/sitemap/src/utils/logger.ts
@@ -1,46 +1,46 @@
// @internal
export interface ILogger {
- info(msg: string): void;
- success(msg: string): void;
- warn(msg: string): void;
- error(msg: string): void;
+ info(msg: string): void;
+ success(msg: string): void;
+ warn(msg: string): void;
+ error(msg: string): void;
}
// @internal
export class Logger implements ILogger {
- private colors = {
- reset: '\x1b[0m',
- fg: {
- red: '\x1b[31m',
- green: '\x1b[32m',
- yellow: '\x1b[33m',
- },
- } as const;
+ private colors = {
+ reset: '\x1b[0m',
+ fg: {
+ red: '\x1b[31m',
+ green: '\x1b[32m',
+ yellow: '\x1b[33m',
+ },
+ } as const;
- private packageName: string;
+ private packageName: string;
- constructor(packageName: string) {
- this.packageName = packageName;
- }
+ constructor(packageName: string) {
+ this.packageName = packageName;
+ }
- private log(msg: string, prefix: string = '') {
- // eslint-disable-next-line no-console
- console.log(`%s${this.packageName}:%s ${msg}\n`, prefix, prefix ? this.colors.reset : '');
- }
+ private log(msg: string, prefix: string = '') {
+ // eslint-disable-next-line no-console
+ console.log(`%s${this.packageName}:%s ${msg}\n`, prefix, prefix ? this.colors.reset : '');
+ }
- info(msg: string) {
- this.log(msg);
- }
+ info(msg: string) {
+ this.log(msg);
+ }
- success(msg: string) {
- this.log(msg, this.colors.fg.green);
- }
+ success(msg: string) {
+ this.log(msg, this.colors.fg.green);
+ }
- warn(msg: string) {
- this.log(`Skipped!\n${msg}`, this.colors.fg.yellow);
- }
+ warn(msg: string) {
+ this.log(`Skipped!\n${msg}`, this.colors.fg.yellow);
+ }
- error(msg: string) {
- this.log(`Failed!\n${msg}`, this.colors.fg.red);
- }
+ error(msg: string) {
+ this.log(`Failed!\n${msg}`, this.colors.fg.red);
+ }
}
diff --git a/packages/integrations/sitemap/src/utils/parse-url.ts b/packages/integrations/sitemap/src/utils/parse-url.ts
index 7d948923a..f9189cf7d 100644
--- a/packages/integrations/sitemap/src/utils/parse-url.ts
+++ b/packages/integrations/sitemap/src/utils/parse-url.ts
@@ -1,28 +1,39 @@
-export const parseUrl = (url: string, defaultLocale: string, localeCodes: string[], base: string) => {
- if (!url || !defaultLocale || localeCodes.length === 0 || localeCodes.some((key) => !key) || !base) {
- throw new Error('parseUrl: some parameters are empty');
- }
- if (url.indexOf(base) !== 0) {
- return undefined;
- }
- let s = url.replace(base, '');
- if (!s || s === '/') {
- return { locale: defaultLocale, path: '/' };
- }
- if (!s.startsWith('/')) {
- s = '/' + s;
- }
- const a = s.split('/');
- const locale = a[1];
- if (localeCodes.some((key) => key === locale)) {
- let path = a.slice(2).join('/');
- if (path === '//') {
- path = '/';
- }
- if (path !== '/' && !path.startsWith('/')) {
- path = '/' + path;
- }
- return { locale, path };
- }
- return { locale: defaultLocale, path: s };
+export const parseUrl = (
+ url: string,
+ defaultLocale: string,
+ localeCodes: string[],
+ base: string
+) => {
+ if (
+ !url ||
+ !defaultLocale ||
+ localeCodes.length === 0 ||
+ localeCodes.some((key) => !key) ||
+ !base
+ ) {
+ throw new Error('parseUrl: some parameters are empty');
+ }
+ if (url.indexOf(base) !== 0) {
+ return undefined;
+ }
+ let s = url.replace(base, '');
+ if (!s || s === '/') {
+ return { locale: defaultLocale, path: '/' };
+ }
+ if (!s.startsWith('/')) {
+ s = '/' + s;
+ }
+ const a = s.split('/');
+ const locale = a[1];
+ if (localeCodes.some((key) => key === locale)) {
+ let path = a.slice(2).join('/');
+ if (path === '//') {
+ path = '/';
+ }
+ if (path !== '/' && !path.startsWith('/')) {
+ path = '/' + path;
+ }
+ return { locale, path };
+ }
+ return { locale: defaultLocale, path: s };
};
diff --git a/packages/integrations/sitemap/src/validate-options.ts b/packages/integrations/sitemap/src/validate-options.ts
new file mode 100644
index 000000000..f89582d82
--- /dev/null
+++ b/packages/integrations/sitemap/src/validate-options.ts
@@ -0,0 +1,22 @@
+import { z } from 'zod';
+import type { SitemapOptions } from './index';
+import { SitemapOptionsSchema } from './schema';
+
+// @internal
+export const validateOptions = (site: string | undefined, opts: SitemapOptions) => {
+ const result = SitemapOptionsSchema.parse(opts);
+
+ z.object({
+ site: z.string().optional(), // Astro takes care of `site`: how to validate, transform and refine
+ canonicalURL: z.string().optional(), // `canonicalURL` is already validated in prev step
+ })
+ .refine(({ site, canonicalURL }) => site || canonicalURL, {
+ message: 'Required `site` astro.config option or `canonicalURL` integration option',
+ })
+ .parse({
+ site,
+ canonicalURL: result.canonicalURL,
+ });
+
+ return result;
+};
diff --git a/packages/integrations/sitemap/src/validate-opts.ts b/packages/integrations/sitemap/src/validate-opts.ts
deleted file mode 100644
index 2a9bfe667..000000000
--- a/packages/integrations/sitemap/src/validate-opts.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { z } from 'zod';
-import type { SitemapOptions } from './index';
-import { SitemapOptionsSchema } from './schema';
-
-// @internal
-export const validateOpts = (site: string | undefined, opts: SitemapOptions) => {
- const schema = SitemapOptionsSchema.extend({
- site: z.string().optional(),
- })
- .strict()
- .refine(({ site, canonicalURL }) => site || canonicalURL, {
- message: 'Required `site` astro.config option or `canonicalURL` integration option',
- });
-
- schema.parse({ site: site || '', ...(opts || {}) });
-};
diff --git a/packages/integrations/sitemap/src/with-options.ts b/packages/integrations/sitemap/src/with-options.ts
deleted file mode 100644
index 6e0982b7b..000000000
--- a/packages/integrations/sitemap/src/with-options.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { isObjectEmpty } from './utils/is-object-empty';
-import type { SitemapOptions } from './index';
-
-const defaultOptions: Readonly = {
- createLinkInHead: true,
- entryLimit: 45000,
-};
-
-// @internal
-export const withOptions = (pluginOptions: SitemapOptions) => {
- if (isObjectEmpty(pluginOptions)) {
- return defaultOptions;
- }
- const options: SitemapOptions = {
- ...pluginOptions,
- createLinkInHead: pluginOptions?.createLinkInHead ?? defaultOptions.createLinkInHead,
- entryLimit: pluginOptions?.entryLimit || defaultOptions.entryLimit,
- };
- return options;
-};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index c5c4fd6c8..d8538418d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1800,11 +1800,9 @@ importers:
specifiers:
astro: workspace:*
astro-scripts: workspace:*
- node-html-parser: ^5.3.3
sitemap: ^7.1.1
zod: ^3.17.3
dependencies:
- node-html-parser: 5.3.3
sitemap: 7.1.1
zod: 3.17.3
devDependencies:
@@ -8075,16 +8073,6 @@ packages:
engines: {node: '>=8'}
dev: true
- /css-select/4.3.0:
- resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==}
- dependencies:
- boolbase: 1.0.0
- css-what: 6.1.0
- domhandler: 4.3.1
- domutils: 2.8.0
- nth-check: 2.1.1
- dev: false
-
/css-select/5.1.0:
resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==}
dependencies:
@@ -8101,6 +8089,7 @@ packages:
/css-what/6.1.0:
resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==}
engines: {node: '>= 6'}
+ dev: true
/cssesc/3.0.0:
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
@@ -9612,6 +9601,7 @@ packages:
/he/1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
+ dev: true
/hosted-git-info/2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
@@ -11123,13 +11113,6 @@ packages:
hasBin: true
dev: false
- /node-html-parser/5.3.3:
- resolution: {integrity: sha512-ncg1033CaX9UexbyA7e1N0aAoAYRDiV8jkTvzEnfd1GDvzFdrsXLzR4p4ik8mwLgnaKP/jyUFWDy9q3jvRT2Jw==}
- dependencies:
- css-select: 4.3.0
- he: 1.2.0
- dev: false
-
/node-pre-gyp/0.13.0:
resolution: {integrity: sha512-Md1D3xnEne8b/HGVQkZZwV27WUi1ZRuZBij24TNaZwUPU3ZAFtvT6xxJGaUVillfmMKnn5oD1HoGsp2Ftik7SQ==}
deprecated: 'Please upgrade to @mapbox/node-pre-gyp: the non-scoped node-pre-gyp package is deprecated and only the @mapbox scoped package will recieve updates in the future'