Skip to content

Commit

Permalink
docs: Document sitemap extension (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
Fryuni authored Mar 9, 2024
1 parent 4c2d618 commit 785f243
Show file tree
Hide file tree
Showing 4 changed files with 211 additions and 46 deletions.
10 changes: 5 additions & 5 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ export default defineConfig({
link: '/custom-routing',
},
{
label: 'Self Declared Sitemap',
link: '/declarative-sitemap',
},
{
label: 'Inline Module',
label: 'Sitemap Extensions',
link: '/sitemap-ext',
badge: {
text: 'NEW',
variant: 'success',
},
},
{
label: 'Inline Module',
autogenerate: { directory: 'inline-mod' },
},
],
Expand Down
33 changes: 0 additions & 33 deletions docs/src/content/docs/declarative-sitemap.md

This file was deleted.

21 changes: 13 additions & 8 deletions docs/src/content/docs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,21 @@ import InvisibleLink from '../../components/InvisibleLink.astro';
## Next steps

<CardGrid stagger>
<InvisibleLink href="/custom-routing">
<Card title="Custom Astro Routing" icon="add-document">
<Card title="Sitemap Extensions" icon="document">
<InvisibleLink href="/sitemap-ext">
An unofficial extension of the official `@astrojs/sitemap` integration.
</InvisibleLink>
</Card>
<Card title="Custom Astro Routing" icon="add-document">
<InvisibleLink href="/custom-routing">
Define custom Astro Routes without relying on file-based routing.
</Card>
</InvisibleLink>
<InvisibleLink href="/inline-mod">
<Card title="Inline Virtual Module" icon="pencil">
</InvisibleLink>
</Card>
<Card title="Inline Virtual Module" icon="pencil">
<InvisibleLink href="/inline-mod">
Pass inline JS values and functions as a virtual module to Vite projects.
</Card>
</InvisibleLink>
</InvisibleLink>
</Card>
{/* <Card title="Add new content" icon="add-document">
Add Markdown or MDX files to `src/content/docs` to create new pages.
</Card>
Expand Down
193 changes: 193 additions & 0 deletions docs/src/content/docs/sitemap-ext.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
---
title: Sitemap Extensions
description: An unofficial extension of the official @astrojs/sitemap integration.
packageName: '@inox-tools/sitemap-ext'
---

An unofficial extension of the official [`@astrojs/sitemap`](https://docs.astro.build/en/guides/integrations-guide/sitemap/) integration that allows:

- Each route to self-declare whether they should be included in the sitemap.
- Including only a subset of pages rendered from a dynamic route in the sitemap.
- On-demand (SSR) routes to include themselves in the sitemap, or some of their known pages if they are dynamic.

The goal of this extension is to provide higher-level functionality and configuration over the official integration. **These extensions are not maintained nor supported by the Astro team.** If some or all of these extensions prove themselves as valuable additions for the users they might be proposed and adopted into the official integration, in that case they would continue to work in this package, but as a thin wrapper or just a re-export from the official package.

## Installation

To install this integration you just need to replace the import from the official `@astrojs/sitemap` integration with `@inox-tools/sitemap-ext`.

```ts title="astro.config.mjs" del={1} add={2}
import sitemap from '@astrojs/sitemap';
import sitemap from '@inox-tools/sitemap-ext';

export default defineConfig({
integration: [sitemap()],
});
```

This integration accepts all the [options from `@astrojs/sitemap`](https://docs.astro.build/en/guides/integrations-guide/sitemap/#configuration), except for the `filter` function that is replaced by the [per-route configuration](#per-route-configuration).

### `includeByDefault`

**Type:** `boolean`
**Default:** `false`

Whether pre-rendered pages that do not explicitly define their presence in the sitemap should be included.

On-demand routes must always explicitly opt-in to being in the sitemap.

## Per-Route configuration

Each of your routes can define how they should be included in your sitemap by importing the module `sitemap-ext:config`.

### Full opt-in/out

For a page to fully opt-in or opt-out of being in the sitemap you can pass a boolean value to the imported module:

```astro
---
import sitemap from 'sitemap-ext:config';
sitemap(true); // opt-in
sitemap(true); // opt-out
---
```

In this case all pages created from the route will follow the given configuration.

:::caution
This applies for all pre-rendered routes and to on-demand _static_ routes (routes that don't have dynamic parameters in their path).

On-demand dynamic routes, like `/[...slug].astro`, can't use the full opt-in since the official sitemap integration that is used under the hood must generate the sitemap statically.
:::

### Configuration callback

For non-trivial scenarios, you can provide a hook function to define how a route should be included in the sitemap.
This hook function will be called during build-time only and will be given callbacks to inform the decisions. It has the following signature:

```ts
type ConfigHook = (callbacks: {
addToSitemap: (routeParams?: Record<string, string | undefined>[]) => void;
removeFromSitemap: (routeParams?: Record<string, string | undefined>[]) => void;
setSitemap: (
routeParams: Array<{ sitemap?: boolean; params: Record<string, string | undefined> }>
) => void;
}) => Promise<void> | void;
```

As noted by the type, the given hook function can be async, which means you can load remote information.

```astro
---
import sitemap from 'sitemap-ext:config';
sitemap(async ({ addToSitemap, removeFromSitemap }) => {
const shouldBeIncluded = await fetchThisConfigFromRemote();
if (shouldBeIncluded) {
addToSitemap();
} else {
removeFromSitemap();
}
});
---
```

Not calling any of the given configuration callbacks means the route will follow what is defined by the global [`includeByDefault` option](#includebydefault)

#### `addToSitemap` / `removeFromSitemap`

Calling these configuration callbacks without any argument has the same meaning as the [full opt in/out](#full-opt-inout) using the boolean values.

These functions also allow passing a list of parameter maps in the argument, these parameter maps are the same as the [`params` field in Astro's `getStaticPaths`](https://docs.astro.build/en/reference/api-reference/#params).

On a static route this argument is ignored. On a dynamic route, this argument is used to replace the dynamic segments and add the resulting URLs to the sitemap. This means you can add some pages generated by an Astro route to the sitemap while keeping others out of it.

```astro title="src/pages/[locale]/[postId].astro"
---
import { getCollection } from 'astro:content';
import sitemap from 'sitemap-ext:config';
sitemap(async ({ addToSitemap }) => {
const blogPosts = await getCollection('posts');
addToSitemap(
blogPosts.map((post) => ({
locale: post.data.locale,
postId: post.data.id,
}))
);
});
---
```

#### `setSitemap`

This function receives an array of parameter maps with a boolean to indicate whether each of those parameters describe a route that should be included or excluded from the sitemap.

If the boolean field is not given it defaults to the global [`includeByDefault` option](#includebydefault)

Calling this function from a static route does nothing.

```astro title="src/pages/[locale]/[postId].astro"
---
import { getCollection } from 'astro:content';
import sitemap from 'sitemap-ext:config';
sitemap(async ({ setSitemap }) => {
const blogPosts = await getCollection('posts');
setSitemap(
blogPosts.map((post) => ({
params: {
locale: post.data.locale,
postId: post.data.id,
},
sitemap: post.data.includeInSitemap,
}))
);
});
---
```

## Interaction with Astro features

### `getStaticPaths`

To avoid writing the same logic twice you can call the sitemap configuration function from inside `getStaticPaths`. This allows you to compute the `params` for your routes only once.

```astro title="src/pages/[locale]/[postId].astro"
---
import { getCollection } from 'astro:content';
import sitemap from 'sitemap-ext:config';
export async function getStaticPaths() {
const blogPosts = await getCollection('posts');
const postParams = blogPosts.map((post) => ({
params: {
locale: post.data.locale,
postId: post.data.id,
},
props: { post }, // for Astro.props
sitemap: post.data.includeInSitemap,
}));
sitemap(({ setSitemap }) => setSitemap(postParams));
return postParams;
}
---
```

### On-demand rendered pages (SSR)

The official sitemap integration doesn't have built-in support for SSR pages, requiring you to re-declare in your configuration all the URLs for server-rendered content that you want included in your sitemap.

Using this extension, on-demand routes can opt into being included in the sitemap. SSR pages on static routes can use the [boolean option](#full-opt-inout) to add themselves to the sitemap. Dynamic routes can only add the pages that are known at build time. This can be useful in many scenarios:

- When you can generate the content for any matching URL, but some of them are well-known.
- When providing [self-healing URLs](https://medium.com/@vishalkamath853/self-healing-urls-eb66756a9c62) to support renaming your slugs over time.
- When your content can change per-request, but not the URLs.

0 comments on commit 785f243

Please sign in to comment.