vuepress-plugin-full-text-search2
Version:
VuePress v2 plugin that adds full-text search box.
201 lines (196 loc) • 6.04 kB
JavaScript
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
default: () => src_default,
fullTextSearchPlugin: () => fullTextSearchPlugin
});
module.exports = __toCommonJS(src_exports);
var import_utils = require("@vuepress/utils");
var import_url = require("url");
// src/prepare-search-index.ts
var import_htmlparser2 = require("htmlparser2");
var HMR_CODE = `
if (import.meta.webpackHot) {
import.meta.webpackHot.accept()
if (__VUE_HMR_RUNTIME__[UPD_NAME]) {
__VUE_HMR_RUNTIME__[UPD_NAME](searchIndex)
}
}
if (import.meta.hot) {
import.meta.hot.accept(({ searchIndex }) => {
if (__VUE_HMR_RUNTIME__[UPD_NAME]) {
__VUE_HMR_RUNTIME__[UPD_NAME](searchIndex)
}
})
}
`;
async function prepareSearchIndex({
app
}) {
const searchIndex = [];
for (const page of app.pages) {
searchIndex.push({
path: page.path,
title: page.title,
pathLocale: page.pathLocale,
contents: extractPageContents(page)
});
}
let content = `
export const searchIndex = ${JSON.stringify(searchIndex, null, 2)}
export const UPD_NAME = 'update-vuepress-plugin-full-text-search2-search-index'
`;
if (app.env.isDev) {
content += HMR_CODE;
}
return app.writeTemp(
"internal/vuepress-plugin-full-text-search2-search-index.js",
content
);
}
function extractPageContents(page) {
const results = [];
const slugs = /* @__PURE__ */ new Map();
const headers = [...page.headers];
while (headers.length) {
const h = headers.shift();
slugs.set(h.slug, h.title);
headers.push(...h.children);
}
let ignoreElement = 0;
let withinHeader = 0;
let scope = {
header: "",
slug: "",
content: ""
};
results.push(scope);
const parser = new import_htmlparser2.Parser({
ontext(text) {
if (ignoreElement) {
return;
}
const prop = withinHeader ? "header" : "content";
scope[prop] += text;
},
onopentag(name, attribute) {
if (ignoreElement || name === "script" || name === "style" || name === "div" && attribute.class === "line-numbers") {
ignoreElement++;
return;
}
if (withinHeader) {
withinHeader++;
return;
}
if (!/^h\d$/u.test(name)) {
return;
}
const id = attribute.id;
const title = slugs.get(id);
if (title) {
scope = {
header: title,
slug: id,
content: ""
};
results.push(scope);
ignoreElement++;
} else {
scope = {
header: "",
slug: id,
content: ""
};
results.push(scope);
withinHeader++;
}
},
onclosetag() {
if (ignoreElement) {
ignoreElement--;
return;
}
if (withinHeader) {
withinHeader--;
}
}
});
parser.parseComplete(page.contentRendered);
return results.map((p) => {
p.header = p.header.replace(/\s{2,}/g, " ").replace(/^#/g, "").trim();
p.content = p.content.replace(/\s{2,}/g, " ").trim();
return p;
}).filter((p) => p.content || p.header);
}
// src/index.ts
var chokidar = __toESM(require("chokidar"), 1);
var import_meta = {};
var filename = typeof __filename !== "undefined" ? __filename : (0, import_url.fileURLToPath)(import_meta.url);
var dirname = import_utils.path.dirname(filename);
var fullTextSearchPlugin = fullTextSearchPluginFunction;
var src_default = fullTextSearchPlugin;
function fullTextSearchPluginFunction(options = {}) {
return {
name: "vuepress-plugin-full-text-search2",
define: {
__SEARCH_LOCALES__: ("locales" in options ? options == null ? void 0 : options.locales : {}) ?? {}
},
clientConfigFile: import_utils.path.resolve(dirname, "./client/clientConfig.js"),
// @ts-expect-error -- Backward compatibility for vuepress@<=2.0.0-beta.43
clientAppEnhanceFiles: import_utils.path.resolve(
dirname,
"./client/clientAppEnhance.js"
),
onPrepared(app) {
prepareSearchIndex({ app });
},
onWatched: (app, watchers) => {
const searchIndexWatcher = chokidar.watch("internal/pageData/*", {
cwd: app.dir.temp(),
ignoreInitial: true
});
searchIndexWatcher.on("add", () => {
prepareSearchIndex({ app });
});
searchIndexWatcher.on("change", () => {
prepareSearchIndex({ app });
});
searchIndexWatcher.on("unlink", () => {
prepareSearchIndex({ app });
});
watchers.push(searchIndexWatcher);
}
};
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
fullTextSearchPlugin
});
;