Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/hip-emus-punch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@astrojs/starlight': patch
---

Allow passing user options to pagefind.createIndex
5 changes: 5 additions & 0 deletions docs/src/content/docs/reference/configuration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,7 @@ Set `pagefind` to an object to configure the Pagefind search client:

- See [“Customize Pagefind's result ranking”](https://pagefind.app/docs/ranking/) in the Pagefind documentation for more details about using the `pagefind.ranking` option to control how search result ranking is calculated
- See [“Searching multiple sites”](https://pagefind.app/docs/multisite/) in the Pagefind documentation for more details about using the `pagefind.mergeIndex` option to control how to search across multiple sites
- See [“pagefind.createIndex”](https://pagefind.app/docs/node-api/#pagefindcreateindex) in the Pagefind documentation for more details about using the `pagefind.index` option to customize what content is indexed.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given we’ll only support excludeSelectors, we can probably describe that here in a tiny bit of detail.


#### `PagefindOptions`

Expand All @@ -529,6 +530,10 @@ interface PagefindOptions {
termSimilarity?: number;
};
}>;
index?: Pick<
pagefind.PagefindServiceConfig,
'rootSelector' | 'excludeSelectors' | 'forceLanguage'
>;
Comment on lines +533 to +536
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m not sure it’s useful to document this using a type from Pagefind which people can’t look up. But with the simplification, it’s probably not too bad to show the actual type? (I’m assuming it’s something like this.)

Suggested change
index?: Pick<
pagefind.PagefindServiceConfig,
'rootSelector' | 'excludeSelectors' | 'forceLanguage'
>;
index?: {
excludeSelectors?: string[];
};

}
```

Expand Down
4 changes: 2 additions & 2 deletions packages/starlight/__tests__/basics/pagefind.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ async function runStarlightPagefind(outputDir: URL) {

vi.mocked(pagefind.createIndex).mockResolvedValue({ index, errors: [] });

await starlightPagefind({ dir: outputDir, logger: new TestAstroIntegrationLogger() });
await starlightPagefind({ dir: outputDir, logger: new TestAstroIntegrationLogger() }, {});

return index;
}
Expand Down Expand Up @@ -61,7 +61,7 @@ test('logs Pagefind errors and closes Pagefind', async () => {
errors: [errorMessage],
});

await expect(starlightPagefind({ dir: outputDir, logger })).rejects.toThrow();
await expect(starlightPagefind({ dir: outputDir, logger }, {})).rejects.toThrow();

expect(logger.error).toHaveBeenCalledWith(`Pagefind error: ${errorMessage}`);

Expand Down
3 changes: 2 additions & 1 deletion packages/starlight/components/Search.astro
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ if (project.trailingSlash === 'never') dataAttributes['data-strip-trailing-slash

<script>
import { pagefindUserConfig } from 'virtual:starlight/pagefind-config';
const { index: _, ...pagefindClientConfig } = pagefindUserConfig;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would still end up leaking the index configuration into the client bundle. It might be better to do this here when creating the virtual module so it isn’t included at all:

'virtual:starlight/pagefind-config': `export const pagefindUserConfig = ${JSON.stringify(opts.pagefind || {})}`,

And updating the virtual module type needs to be done manually too here:

declare module 'virtual:starlight/pagefind-config' {
export const pagefindUserConfig: Partial<
Extract<import('./types').StarlightConfig['pagefind'], object>
>;
}


class SiteSearch extends HTMLElement {
constructor() {
Expand Down Expand Up @@ -139,7 +140,7 @@ if (project.trailingSlash === 'never') dataAttributes['data-strip-trailing-slash
// @ts-expect-error — Missing types for @pagefind/default-ui package.
const { PagefindUI } = await import('@pagefind/default-ui');
new PagefindUI({
...pagefindUserConfig,
...pagefindClientConfig,
element: '#starlight__search',
baseUrl: import.meta.env.BASE_URL,
bundlePath: import.meta.env.BASE_URL.replace(/\/$/, '') + '/pagefind/',
Expand Down
2 changes: 1 addition & 1 deletion packages/starlight/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export default function StarlightIntegration(

'astro:build:done': async (options) => {
if (!userConfig.pagefind) return;
return starlightPagefind(options);
return starlightPagefind(options, userConfig.pagefind);
},
},
};
Expand Down
11 changes: 6 additions & 5 deletions packages/starlight/integrations/pagefind.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import type { HookParameters } from 'astro';
import { fileURLToPath } from 'node:url';
import * as pagefind from 'pagefind';
import type { StarlightConfig } from '../types';

/** Run Pagefind to generate search index files based on the build output directory. */
export async function starlightPagefind({
dir,
logger: starlightLogger,
}: PagefindIntegrationOptions) {
export async function starlightPagefind(
{ dir, logger: starlightLogger }: PagefindIntegrationOptions,
userConfig: Pick<Extract<StarlightConfig['pagefind'], object>, 'index'>
) {
const logger = starlightLogger.fork('starlight:pagefind');
const options = { dir, logger };

try {
const now = performance.now();
logger.info('Building search index with Pagefind...');

const newIndexResponse = await pagefind.createIndex();
const newIndexResponse = await pagefind.createIndex(userConfig.index);

const { index } = assertPagefindResponse<pagefind.NewIndexResponse>(newIndexResponse, options);

Expand Down
18 changes: 18 additions & 0 deletions packages/starlight/schemas/pagefind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ const pagefindIndexOptionsSchema = z.object({
ranking: pagefindRankingWeightsSchema.default({}),
});

const pagefindIndexSchema = z
.object({
rootSelector: z.string().optional(),
excludeSelectors: z.array(z.string()).optional(),
forceLanguage: z.string().optional(),
// verbose, logfile omitted as they don’t affect the output index
// keepIndexUrl omitted for framework convention
})
// convert value-optional types to strictly property-optional types to satisfy exactOptionalPropertyTypes when passing to pagefind
// TODO: once zod>=4.3.0 is available we can use .exactOptional() instead, and remove this
.transform(({ rootSelector, excludeSelectors, forceLanguage }) => ({
...(rootSelector ? { rootSelector } : {}),
...(excludeSelectors ? { excludeSelectors } : {}),
...(forceLanguage ? { forceLanguage } : {}),
}));

const pagefindSchema = z.object({
/**
* Configure how search results from the current website are weighted by Pagefind
Expand Down Expand Up @@ -103,6 +119,8 @@ const pagefindSchema = z.object({
})
)
.optional(),

index: pagefindIndexSchema.optional(),
});

export const PagefindConfigSchema = () => pagefindSchema;
Expand Down