diff --git a/.eslintignore b/.eslintignore
index d525538fd8..a28f4736e0 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -5,6 +5,11 @@ node_modules
*.d.ts
packages/cli/src/lib/live-server/*
+packages/cli/test/**/pagefind/**/*.js
+
+
+# Docs
+docs/_site/
# --- packages/core ---
diff --git a/.gitignore b/.gitignore
index 354522f152..3a43278b04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,6 +46,9 @@ packages/core/template/*/_site
# Generated site (MarkBind)
packages/cli/test/functional/*/_site
+# Generated pagefind directories for sites and in expected
+packages/cli/test/functional/**/pagefind
+
# Ignore .page-vue-render.js files in functional test and subdirectories on update
packages/cli/test/functional/**/*.page-vue-render.js
@@ -107,6 +110,8 @@ packages/core/index.js
packages/core/src/lib/progress/*.js
# --- packages/core end ---
-# Nx for Lerna
.nx/cache
-.nx/workspace-data
\ No newline at end of file
+.nx/workspace-data
+
+# Pagefind fragments
+*.pf_fragment
\ No newline at end of file
diff --git a/docs/userGuide/makingTheSiteSearchable.md b/docs/userGuide/makingTheSiteSearchable.md
index 4e28680c49..e5ef64f9fb 100644
--- a/docs/userGuide/makingTheSiteSearchable.md
+++ b/docs/userGuide/makingTheSiteSearchable.md
@@ -37,6 +37,35 @@ You can add a search bar component to your website to allow users to search the
+
+
+## Using Pagefind (Beta)
+
+MarkBind now supports [Pagefind](https://pagefind.app/), a static low-bandwidth search library, as a built-in feature. This provides full-text search capabilities without external services.
+
+
+This is a beta feature and will be refined in future updates. To use it, you must have enableSearch: true in your site.json (this is the default).
+
+
+
+The Pagefind index is currently only generated during a full site build (e.g., markbind build). It will not repeatedly update during live reload (markbind serve) when you modify pages. You must restart the server (re-run markbind serve) or rebuild to refresh the search index.
+
+
+To add the Pagefind search bar to your page, simply insert the following `div` where you want it to appear:
+
+```html
+
+```
+
+MarkBind will automatically inject the necessary scripts and styles to render the search UI.
+
+The following UI will be rendered, which is provided by Pagefind:
+
+
+
+
+
+
## Using External Search Services
MarkBind sites can use Algolia Doc Search services easily via the Algolia plugin. Unlike the built-in search, Algolia provides full-text search. See the panel below for more info.
diff --git a/package-lock.json b/package-lock.json
index b581c17151..45f64ae721 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4326,6 +4326,84 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@pagefind/darwin-arm64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz",
+ "integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@pagefind/darwin-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz",
+ "integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@pagefind/freebsd-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz",
+ "integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@pagefind/linux-arm64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz",
+ "integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@pagefind/linux-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz",
+ "integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@pagefind/windows-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz",
+ "integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -17172,6 +17250,23 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/pagefind": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz",
+ "integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==",
+ "license": "MIT",
+ "bin": {
+ "pagefind": "lib/runner/bin.cjs"
+ },
+ "optionalDependencies": {
+ "@pagefind/darwin-arm64": "1.4.0",
+ "@pagefind/darwin-x64": "1.4.0",
+ "@pagefind/freebsd-x64": "1.4.0",
+ "@pagefind/linux-arm64": "1.4.0",
+ "@pagefind/linux-x64": "1.4.0",
+ "@pagefind/windows-x64": "1.4.0"
+ }
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -24523,6 +24618,7 @@
"material-icons": "^1.9.1",
"moment": "^2.29.4",
"nunjucks": "3.2.4",
+ "pagefind": "^1.4.0",
"path-is-inside": "^1.0.2",
"simple-git": "^3.22.0",
"url-parse": "^1.5.10",
@@ -29865,6 +29961,7 @@
"memfs": "^3.0.1",
"moment": "^2.29.4",
"nunjucks": "3.2.4",
+ "pagefind": "^1.4.0",
"path-is-inside": "^1.0.2",
"simple-git": "^3.22.0",
"ts-jest": "^29.4.6",
@@ -32711,6 +32808,42 @@
"integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==",
"dev": true
},
+ "@pagefind/darwin-arm64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz",
+ "integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==",
+ "optional": true
+ },
+ "@pagefind/darwin-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz",
+ "integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==",
+ "optional": true
+ },
+ "@pagefind/freebsd-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz",
+ "integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==",
+ "optional": true
+ },
+ "@pagefind/linux-arm64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz",
+ "integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==",
+ "optional": true
+ },
+ "@pagefind/linux-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz",
+ "integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==",
+ "optional": true
+ },
+ "@pagefind/windows-x64": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz",
+ "integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==",
+ "optional": true
+ },
"@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -43303,6 +43436,19 @@
}
}
},
+ "pagefind": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz",
+ "integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==",
+ "requires": {
+ "@pagefind/darwin-arm64": "1.4.0",
+ "@pagefind/darwin-x64": "1.4.0",
+ "@pagefind/freebsd-x64": "1.4.0",
+ "@pagefind/linux-arm64": "1.4.0",
+ "@pagefind/linux-x64": "1.4.0",
+ "@pagefind/windows-x64": "1.4.0"
+ }
+ },
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
diff --git a/packages/cli/test/functional/test.js b/packages/cli/test/functional/test.js
index 77366ae900..58df4a1b2f 100644
--- a/packages/cli/test/functional/test.js
+++ b/packages/cli/test/functional/test.js
@@ -43,12 +43,14 @@ expectedErrors.forEach((error, index) => {
logger.info(`${index + 1}: ${error}`);
});
+const GENERATED_DIRECTORIES_TO_IGNORE = ['pagefind'];
+
testSites.forEach((siteName) => {
console.log(`Running ${siteName} tests`);
try {
execSync(`node ../../index.js build ${siteName}`, execOptions);
const siteIgnoredFiles = plantumlGeneratedFilesForTestSites[siteName];
- compare(siteName, 'expected', '_site', siteIgnoredFiles);
+ compare(siteName, 'expected', '_site', siteIgnoredFiles, GENERATED_DIRECTORIES_TO_IGNORE);
} catch (err) {
printFailedMessage(err, siteName);
process.exit(1);
@@ -63,7 +65,8 @@ testConvertSites.forEach((sitePath) => {
execSync(`node ../../index.js init ${nonMarkBindSitePath} -c`, execOptions);
execSync(`node ../../index.js build ${nonMarkBindSitePath}`, execOptions);
const siteIgnoredFiles = plantumlGeneratedFilesForConvertSites[siteName];
- compare(sitePath, 'expected', 'non_markbind_site/_site', siteIgnoredFiles);
+ compare(sitePath, 'expected', 'non_markbind_site/_site', siteIgnoredFiles,
+ GENERATED_DIRECTORIES_TO_IGNORE);
} catch (err) {
printFailedMessage(err, sitePath);
cleanupConvert(path.resolve(__dirname, sitePath));
@@ -83,7 +86,7 @@ testTemplateSites.forEach((templateAndSitePath) => {
execSync(`node ../../index.js init ${siteCreationTempPath} --template ${flag}`, execOptions);
execSync(`node ../../index.js build ${siteCreationTempPath}`, execOptions);
const siteIgnoredFiles = plantumlGeneratedFilesForTemplateSites[siteName];
- compare(sitePath, 'expected', 'tmp/_site', siteIgnoredFiles);
+ compare(sitePath, 'expected', 'tmp/_site', siteIgnoredFiles, GENERATED_DIRECTORIES_TO_IGNORE);
} catch (err) {
printFailedMessage(err, sitePath);
fs.removeSync(path.resolve(__dirname, siteCreationTempPath));
diff --git a/packages/cli/test/functional/testUtil/compare.js b/packages/cli/test/functional/testUtil/compare.js
index 22e75421c3..abfa70fa92 100644
--- a/packages/cli/test/functional/testUtil/compare.js
+++ b/packages/cli/test/functional/testUtil/compare.js
@@ -43,13 +43,19 @@ function filterPageVueRenderFiles(filePaths) {
* @param {string} expectedSiteRelativePath - Relative path to expected site output (default: "expected")
* @param {string} siteRelativePath - Relative path to actual generated site output (default: "_site")
* @param {string} ignoredPaths - Specify any paths to ignore for comparison, but still check for existence.
+ * @param {string[]} ignoredDirectories - Specify any directories to ignore for comparison (e.g. 'pagefind')
*/
-function compare(root, expectedSiteRelativePath = 'expected', siteRelativePath = '_site', ignoredPaths = []) {
+function compare(root,
+ expectedSiteRelativePath = 'expected',
+ siteRelativePath = '_site',
+ ignoredPaths = [],
+ ignoredDirectories = []) {
const expectedDirectory = path.join(root, expectedSiteRelativePath);
const actualDirectory = path.join(root, siteRelativePath);
- let expectedPaths = walkSync(expectedDirectory, { directories: false });
- let actualPaths = walkSync(actualDirectory, { directories: false });
+ const walkSyncOptions = { directories: false, ignore: ignoredDirectories };
+ let expectedPaths = walkSync(expectedDirectory, walkSyncOptions);
+ let actualPaths = walkSync(actualDirectory, walkSyncOptions);
// Vue render JS files (*.page-vue-render.js) are not committed to version control,
// so we exclude them from the comparison to avoid false positive diffs.
diff --git a/packages/cli/test/functional/test_site/expected/bugs/index.html b/packages/cli/test/functional/test_site/expected/bugs/index.html
index 8a52e0ef6e..c9cc28a86d 100644
--- a/packages/cli/test/functional/test_site/expected/bugs/index.html
+++ b/packages/cli/test/functional/test_site/expected/bugs/index.html
@@ -16,6 +16,7 @@
+
@@ -359,5 +360,21 @@