docusaurus-plugin-rangefind 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +127 -0
- package/index.js +113 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# docusaurus-plugin-rangefind
|
|
2
|
+
|
|
3
|
+
Add fast, fully static search to a [Docusaurus](https://docusaurus.io) site with
|
|
4
|
+
[Rangefind](https://github.com/xjodoin/rangefind) — a search engine that ships a
|
|
5
|
+
**static index** and answers queries in the browser with **HTTP Range
|
|
6
|
+
requests**. No search server, no third-party service, no crawl-time API keys.
|
|
7
|
+
|
|
8
|
+
At `docusaurus build` time the plugin:
|
|
9
|
+
|
|
10
|
+
1. Crawls the freshly built HTML in `outDir` and writes a Rangefind index to
|
|
11
|
+
`build/rangefind/` (via `buildFromCrawl` from `rangefind/crawler`).
|
|
12
|
+
2. Copies the drop-in search web component and its optional theme into
|
|
13
|
+
`build/_rangefind/`.
|
|
14
|
+
3. Injects the component's `<script type="module">` (and, by default, the theme
|
|
15
|
+
`<link rel="stylesheet">`) on **every page**.
|
|
16
|
+
|
|
17
|
+
You then place the `<rangefind-search>` box wherever you want it.
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install --save-dev docusaurus-plugin-rangefind rangefind
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`rangefind` is a runtime peer used at build time (crawler + client bundle);
|
|
26
|
+
`@docusaurus/core` >= 3 is a peer.
|
|
27
|
+
|
|
28
|
+
## Configure
|
|
29
|
+
|
|
30
|
+
`docusaurus.config.js`:
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
module.exports = {
|
|
34
|
+
// ...
|
|
35
|
+
plugins: ["docusaurus-plugin-rangefind"]
|
|
36
|
+
};
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Or with options:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
module.exports = {
|
|
43
|
+
plugins: [
|
|
44
|
+
["docusaurus-plugin-rangefind", { theme: true }]
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Place the search box
|
|
50
|
+
|
|
51
|
+
The plugin injects the component's script/style globally but **does not** render
|
|
52
|
+
a search box for you — that would force a duplicate box onto every page with no
|
|
53
|
+
control over placement (this mirrors how other Docusaurus search plugins work).
|
|
54
|
+
Instead, `<rangefind-search>` is a plain custom element, so you drop it in
|
|
55
|
+
wherever you like. Point its `src` at the index, which lives at
|
|
56
|
+
`<baseUrl>rangefind/` (so `/rangefind/` for a root deploy, `/my-repo/rangefind/`
|
|
57
|
+
under a sub-path `baseUrl`).
|
|
58
|
+
|
|
59
|
+
**Option A — a navbar item (recommended).** Use Docusaurus's built-in `html`
|
|
60
|
+
navbar item type:
|
|
61
|
+
|
|
62
|
+
```js
|
|
63
|
+
themeConfig: {
|
|
64
|
+
navbar: {
|
|
65
|
+
items: [
|
|
66
|
+
{
|
|
67
|
+
type: "html",
|
|
68
|
+
position: "right",
|
|
69
|
+
value: '<rangefind-search src="/rangefind/" placeholder="Search…"></rangefind-search>'
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Option B — directly in any Markdown/MDX page.** Because it is a custom
|
|
77
|
+
element, no React wrapper is needed once the script is injected:
|
|
78
|
+
|
|
79
|
+
```mdx
|
|
80
|
+
<rangefind-search src="/rangefind/" placeholder="Search the docs…"></rangefind-search>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Option C — a swizzled component.** Swizzle e.g. `Navbar/Content` and render
|
|
84
|
+
`<rangefind-search src="/rangefind/" />` in your JSX. React passes custom
|
|
85
|
+
elements through untouched.
|
|
86
|
+
|
|
87
|
+
## Options
|
|
88
|
+
|
|
89
|
+
All options are optional.
|
|
90
|
+
|
|
91
|
+
| Option | Type | Default | Effect |
|
|
92
|
+
| --- | --- | --- | --- |
|
|
93
|
+
| `enabled` | `boolean` | `true` | Set `false` to skip indexing and injection entirely (e.g. in a preview build). |
|
|
94
|
+
| `theme` | `boolean` | `true` | Inject the optional `rangefind-search.css` theme link. Set `false` if you style the component yourself. |
|
|
95
|
+
| `baseUrl` | `string` | site `baseUrl` | URL prefix for result URLs and the injected asset tags. Defaults to Docusaurus's own `baseUrl`, which is correct for sub-path deploys. |
|
|
96
|
+
| `outputDir` | `string` | `"rangefind"` | Index directory, relative to `outDir` (absolute paths are used as-is). This is also where your `<rangefind-search src>` should point (`<baseUrl><outputDir>/`). |
|
|
97
|
+
| `assetsDir` | `string` | `"_rangefind"` | Directory (relative to `outDir`) for the copied `rangefind-search.js` / `.css`. |
|
|
98
|
+
|
|
99
|
+
## Styling
|
|
100
|
+
|
|
101
|
+
Zero framework is required beyond Docusaurus. The `<rangefind-search>` element
|
|
102
|
+
renders into its **light DOM** with no styling of its own, so:
|
|
103
|
+
|
|
104
|
+
- The bundled theme (`theme: true`, on by default) gives you a usable look with
|
|
105
|
+
light/dark palettes out of the box.
|
|
106
|
+
- With Tailwind or any CSS framework, disable the theme (`theme: false`) and use
|
|
107
|
+
the component's per-part `*-class` attributes (`input-class`, `option-class`,
|
|
108
|
+
`mark-class`, …) or namespaced `rf-search__*` hook classes.
|
|
109
|
+
|
|
110
|
+
See the Rangefind monorepo's component reference (the "Search UI component"
|
|
111
|
+
section of `docs/reference.md`) for the full attribute list, class hooks,
|
|
112
|
+
events, and accessibility details — this plugin does not change that API, it
|
|
113
|
+
only wires the component into the Docusaurus build.
|
|
114
|
+
|
|
115
|
+
## How it works
|
|
116
|
+
|
|
117
|
+
- **Hooks:** the plugin uses the official `postBuild(props)` lifecycle hook
|
|
118
|
+
(crawl + asset copy, keyed off `props.outDir`) and `injectHtmlTags()`
|
|
119
|
+
(global script + optional style in `postBodyTags`).
|
|
120
|
+
- **Asset resolution:** the client bundle and theme are located with
|
|
121
|
+
`require.resolve("rangefind/element")` and
|
|
122
|
+
`require.resolve("rangefind/element.css")`, so they always match the installed
|
|
123
|
+
`rangefind` version; the crawler is loaded via a dynamic
|
|
124
|
+
`import("rangefind/crawler")`.
|
|
125
|
+
- **Deployment:** Rangefind needs a static host that supports HTTP `Range`
|
|
126
|
+
requests (GitHub Pages, Netlify, S3/CloudFront, nginx, …). See the Rangefind
|
|
127
|
+
deployment notes for MIME/caching guidance.
|
package/index.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// docusaurus-plugin-rangefind — index a built Docusaurus site with Rangefind
|
|
4
|
+
// (a static search engine: static index + HTTP Range requests, no server) and
|
|
5
|
+
// inject the drop-in `<rangefind-search>` web component site-wide.
|
|
6
|
+
//
|
|
7
|
+
// This module is CommonJS on purpose: Docusaurus loads plugin modules with a
|
|
8
|
+
// CJS-first loader, and `require.resolve` gives us the real on-disk paths of
|
|
9
|
+
// the `rangefind/element` bundle and its CSS without executing them. The
|
|
10
|
+
// crawler (`rangefind/crawler`) is ESM in an otherwise-ESM package, so it is
|
|
11
|
+
// pulled in with a dynamic `import()` from inside the async `postBuild` hook —
|
|
12
|
+
// that works from CJS regardless of the target module's format.
|
|
13
|
+
|
|
14
|
+
const fs = require("node:fs");
|
|
15
|
+
const path = require("node:path");
|
|
16
|
+
|
|
17
|
+
const DEFAULT_OUTPUT_DIR = "rangefind"; // index dir, relative to outDir
|
|
18
|
+
const DEFAULT_ASSETS_DIR = "_rangefind"; // client asset dir, relative to outDir
|
|
19
|
+
const ASSET_JS = "rangefind-search.js";
|
|
20
|
+
const ASSET_CSS = "rangefind-search.css";
|
|
21
|
+
|
|
22
|
+
// Docusaurus base URLs always carry a trailing slash; normalize so callers can
|
|
23
|
+
// pass "/base" or "/base/" interchangeably and our string joins never double
|
|
24
|
+
// or drop the separator.
|
|
25
|
+
function normalizeBaseUrl(baseUrl) {
|
|
26
|
+
const value = String(baseUrl || "/");
|
|
27
|
+
return value.endsWith("/") ? value : `${value}/`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
module.exports = function rangefindPlugin(context = {}, options = {}) {
|
|
31
|
+
const enabled = options.enabled !== false;
|
|
32
|
+
const theme = options.theme !== false;
|
|
33
|
+
const assetsDir = options.assetsDir || DEFAULT_ASSETS_DIR;
|
|
34
|
+
const outputDir = options.outputDir || DEFAULT_OUTPUT_DIR;
|
|
35
|
+
|
|
36
|
+
// Base URL for the injected asset tags. Prefer an explicit override, then the
|
|
37
|
+
// site's own configured baseUrl so the tag src matches the deployed prefix.
|
|
38
|
+
const siteBaseUrl = normalizeBaseUrl(
|
|
39
|
+
options.baseUrl != null ? options.baseUrl : context && context.siteConfig && context.siteConfig.baseUrl
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
name: "docusaurus-plugin-rangefind",
|
|
44
|
+
|
|
45
|
+
// Official post-build lifecycle hook: the whole site is already emitted to
|
|
46
|
+
// props.outDir, so we crawl that HTML into a Rangefind index and copy the
|
|
47
|
+
// search UI assets alongside it.
|
|
48
|
+
async postBuild(props) {
|
|
49
|
+
if (!enabled) return;
|
|
50
|
+
|
|
51
|
+
const outDir = props.outDir;
|
|
52
|
+
// buildFromCrawl's baseUrl is the URL prefix stamped onto every result
|
|
53
|
+
// URL; Docusaurus's own baseUrl is the right default so links resolve on
|
|
54
|
+
// the deployed site (root-relative under any sub-path deploy).
|
|
55
|
+
const crawlBaseUrl = normalizeBaseUrl(
|
|
56
|
+
options.baseUrl != null
|
|
57
|
+
? options.baseUrl
|
|
58
|
+
: props.baseUrl != null
|
|
59
|
+
? props.baseUrl
|
|
60
|
+
: props.siteConfig && props.siteConfig.baseUrl != null
|
|
61
|
+
? props.siteConfig.baseUrl
|
|
62
|
+
: siteBaseUrl
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const output = path.isAbsolute(outputDir) ? outputDir : path.join(outDir, outputDir);
|
|
66
|
+
|
|
67
|
+
// ESM crawler pulled in dynamically from this CJS module.
|
|
68
|
+
const { buildFromCrawl } = await import("rangefind/crawler");
|
|
69
|
+
await buildFromCrawl({
|
|
70
|
+
root: outDir,
|
|
71
|
+
scanDir: outDir,
|
|
72
|
+
output,
|
|
73
|
+
baseUrl: crawlBaseUrl
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Copy the two client assets next to the site. Paths resolve through the
|
|
77
|
+
// `rangefind` package exports map to the real dist files.
|
|
78
|
+
const assetsOut = path.join(outDir, assetsDir);
|
|
79
|
+
await fs.promises.mkdir(assetsOut, { recursive: true });
|
|
80
|
+
const jsSrc = require.resolve("rangefind/element");
|
|
81
|
+
const cssSrc = require.resolve("rangefind/element.css");
|
|
82
|
+
await fs.promises.copyFile(jsSrc, path.join(assetsOut, ASSET_JS));
|
|
83
|
+
await fs.promises.copyFile(cssSrc, path.join(assetsOut, ASSET_CSS));
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// Inject the search element's script (and, unless disabled, the optional
|
|
87
|
+
// theme) on every page. The `<rangefind-search>` element itself is NOT
|
|
88
|
+
// injected here — users place it where they want (navbar html item, MDX,
|
|
89
|
+
// or a swizzled component); see the README.
|
|
90
|
+
injectHtmlTags() {
|
|
91
|
+
if (!enabled) return {};
|
|
92
|
+
const tags = [
|
|
93
|
+
{
|
|
94
|
+
tagName: "script",
|
|
95
|
+
attributes: {
|
|
96
|
+
type: "module",
|
|
97
|
+
src: `${siteBaseUrl}${assetsDir}/${ASSET_JS}`
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
];
|
|
101
|
+
if (theme) {
|
|
102
|
+
tags.push({
|
|
103
|
+
tagName: "link",
|
|
104
|
+
attributes: {
|
|
105
|
+
rel: "stylesheet",
|
|
106
|
+
href: `${siteBaseUrl}${assetsDir}/${ASSET_CSS}`
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return { postBodyTags: tags };
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "docusaurus-plugin-rangefind",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Docusaurus plugin that indexes your built site with Rangefind (static search over HTTP Range requests, no server) and injects the drop-in <rangefind-search> web component.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Xavier Jodoin <xavier@jodoin.me>",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"docusaurus",
|
|
9
|
+
"docusaurus-plugin",
|
|
10
|
+
"search",
|
|
11
|
+
"rangefind",
|
|
12
|
+
"static-search",
|
|
13
|
+
"client-side-search"
|
|
14
|
+
],
|
|
15
|
+
"main": "index.js",
|
|
16
|
+
"files": [
|
|
17
|
+
"index.js",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"test": "node --test test/*.test.js"
|
|
22
|
+
},
|
|
23
|
+
"peerDependencies": {
|
|
24
|
+
"@docusaurus/core": ">=3.0.0"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"rangefind": "^0.3.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@docusaurus/core": "^3",
|
|
31
|
+
"@docusaurus/preset-classic": "^3",
|
|
32
|
+
"react": "^18.3.1",
|
|
33
|
+
"react-dom": "^18.3.1"
|
|
34
|
+
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/xjodoin/rangefind.git",
|
|
38
|
+
"directory": "packages/docusaurus-plugin-rangefind"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/xjodoin/rangefind/tree/main/packages/docusaurus-plugin-rangefind#readme",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/xjodoin/rangefind/issues"
|
|
43
|
+
}
|
|
44
|
+
}
|