Performing a Code Reading of Docusaurus
Published: 2023-10-30 04:27:51

For this week in [Topics in Open Source Development](https://github.com/Seneca-CDOT/topics-in-open-source-2023), I was tasked with performing a code reading of [Docusaurus](https://docusaurus.io/), a popular documentation static site generator.

Docusaurus provides the ability for users to search through documentation. Instead of bootstrapping its own search function, it leverages existing solutions which the developer can choose from, including [Algolia DocSearch](https://docsearch.algolia.com/), [Typesense](https://typesense.org/), a variety of [community plugins](https://docusaurus.io/community/resources#search), or one's own search solution. Since Docusaurus does not provide a native search function and relies on third party solutions, this code reading will focus on how it integrates the Algolia Docsearch search functionality into its UI.

Docusaurus Repository: https://github.com/facebook/docusaurus

## Documentation

The documentation to configure a search function for one's website is found in [website/docs/search.mdx](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/website/docs/search.mdx). This same page is hosted officially at https://docusaurus.io/docs/search.

It is a [MDX](https://mdxjs.com/) file that combines both markdown and JSX syntax, though it does not use MDX specific syntax. It does however use Front Matter syntax for use with the [Front Matter](https://frontmatter.codes/docs/markdown) extension, adding a `keywords` attribute in a front matter block as its header, which defines parseable metadata.

```
---
keywords:
  - algolia
  - search
---
...
```

I haven't worked with MDX or Front Matter before, but I've seen (remarkably) similar syntax with Obsidian's [Tags](****) syntax. This provides a way to tag and parse otherwise simple markdown files in a powerful, programmatic way.

## Docusaurus Classic Preset

Docusaurus features a set of presets - bundles of packages and plugins. Of note, the [classic preset](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-preset-classic/src/index.ts) supports the Algolia DocSearch by default. It does so by incorporating the `theme-search-algolia` theme.

```ts
export default function preset(
  context: LoadContext,
  opts: Options = {},
): Preset {
  const {siteConfig} = context;
  const {themeConfig} = siteConfig;
  const {algolia} = themeConfig as Partial<ThemeConfig>;
  const isProd = process.env.NODE_ENV === 'production';
  const {
    debug,
    docs,
    blog,
    pages,
    sitemap,
    theme,
    googleAnalytics,
    gtag,
    googleTagManager,
    …rest
  } = opts;

  const themes: PluginConfig[] = [];
  themes.push(makePluginConfig('@docusaurus/theme-classic', theme));
  if (algolia) {
    themes.push(require.resolve('@docusaurus/theme-search-algolia'));
  }
  ...
}
```

If a configuration object (resolved into the `algolia` `Partial<ThemeConfig>`) is not provided when instantiating this preset, the preset will not include the `theme-search-algolia` theme. This is due to the fact that usage of the Algolia API requires the API variables to be defined in `docusaurus.config.js` (details [here](https://docusaurus.io/docs/search#connecting-algolia)).

## Docusaurus Algolia Search Theme

The Algolia search theme package is found in [/packages/docusaurus-theme-search-algolia](https://github.com/facebook/docusaurus/tree/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia). The package module and its types are defined in the [theme-search-algolia.d.ts](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme-search-algolia.d.ts) file. I've seen and used similar instances of declaring types (and their subtypes) in their own dedicated file for visibility.

The [docusaurus-theme-search-algolia/src/index.ts](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/index.ts) file defines the function `themeSearchAlgolia` which returns the instantiated plugin of type `Plugin<void>`, which is a type used to define Docusaurus plugins and contains data members such as its name, webpack configuration, file paths, HTML tags to inject, and translation files. Of note, it takes in a `themeConfig` object which includes the Algolia API configuration data.

In this package's `src` folder there are the following directories:

- `__tests__`
	- Jest tests including:
		- A minimal configuration using a sample Algolia configuration (including a sample Algolia key and Algolia application ID) ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.ts#L29)).
		- Passing an unknown key in this configuration ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.ts#L43C3-L43C3)).
		- If the Algolia configuration object is undefined ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.ts#L58C7-L58C8)).
		- If the API key is missing ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.ts#L78)).
- `client`
	- A function to retrieve the theme configuration object ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/client/useAlgoliaThemeConfig.ts)).
	- A function to parse URLs returned from Algolia into local paths that can be navigated to within Docusaurus. This is done by first checking whether it is an external domain with a regex pattern match, then converting it to a relative URL ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/client/useSearchResultUrlProcessor.ts)).
- `templates`
	- Contains an HTML tag to integrate with the [OpenSearch API](https://opensearch.org/) ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/templates/opensearch.ts)).
- `theme`
	- The Search Bar React component ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx))
		- Returns a custom DocSearch component ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx#L268C11-L268C21)).
		- This component includes a property titled `transformSearchClient` ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx#L251)) which calls the `addAlgoliaAgent` method from the `algoliasearch/lite` package, which adds a custom user agent to interact with Algolia API ([link](https://www.algolia.com/doc/api-reference/api-methods/add-algolia-agent/)).
		- This search bar component is meant to be inserted into the navbar component for a theme, such as the Docusarus classic theme's navbar component ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-classic/src/theme/Navbar/Content/index.tsx#L90)).
		- To "prepare" the browser to load subsequent queries from the Algolia API, a `<link>` tag referencing the Algolia service ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.tsx#L224)) is placed in the `<Head>` component that is not actually rendered to the client and serves no other purpose.
	- The Search Page React component ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx))
		- A dedicated search page component.
		- An Algolia client is constructed using the `appId` and `apiKey` stored in the theme configuration object ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx#L216)).
		- To use this client in a meaningful way, an Algolia helper is constructed where the parent component can define options including how many results to show per page ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx#L217)).
			- Upon receiving a search result ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx#L223)), a callback function is called that sanitizes the returned result and parses the result array via the JavaScript map method, returning an array of objects that includes the title, URL, a textual summary, and a trail of breadcrumbs to follow.
			- The `searchResultStateDispatcher` method is called with the returned and parsed search results to be stored in state.
		- Renders a dedicated link to `www.algolia.com` ([link](https://github.com/facebook/docusaurus/blob/495c7936b605e6b8881f000d23a8463a9d6d850c/packages/docusaurus-theme-search-algolia/src/theme/SearchPage/index.tsx#L424)).

## Code Reading Techniques

To perform this code reading, I opened this project in VS Code which I am more comfortable with instead of using GitHub code search or `git grep`. The "search all" function was the most useful, as well as "Go To Definition" to see where a function alias was originally defined. I started by searching for the instances of the word `search` throughout the project to find modules that were relevant to the search function. Next, I observed the file and folder structure to understand how the project was laid out.

Directories of note include:

- `__tests__`
	- Tests specific to validating the `packages.json` and `tsconfig.json` files.
- `admin`
	- Instructions for environment setup, testing, debugging, publishing, and other miscellaneous files.
- `examples`
	- Example Docusaurus projects.
- `packages`
	- Docusaurus packages including presets, scripts, plugins, types, utility functions, and themes.
- `website`
	- The official Docusaurus website to be rendered and hosted. Acts as the single source of truth for Docusaurus documentation.

If there was any code that was unfamiliar to me (such as MDX syntax), I pasted it into ChatGPT to identify the type of syntax I was looking at (if I could not discern it from the filename extension). I also looked through the official Docusaurus documentation on how to implement the search function for more clues.

_Thanks for reading!_