| #!/usr/bin/env node | |
| const fs = require("fs"); | |
| const path = require("path"); | |
| const targetSearchBarFile = path.join( | |
| process.cwd(), | |
| "node_modules", | |
| "@yang1666204", | |
| "docusaurus-search-local", | |
| "dist", | |
| "client", | |
| "client", | |
| "theme", | |
| "SearchBar", | |
| "SearchBar.jsx" | |
| ); | |
| const targetPostBuildFactoryFile = path.join( | |
| process.cwd(), | |
| "node_modules", | |
| "@yang1666204", | |
| "docusaurus-search-local", | |
| "dist", | |
| "server", | |
| "server", | |
| "utils", | |
| "postBuildFactory.js" | |
| ); | |
| const targetSearchPageFile = path.join( | |
| process.cwd(), | |
| "node_modules", | |
| "@yang1666204", | |
| "docusaurus-search-local", | |
| "dist", | |
| "client", | |
| "client", | |
| "theme", | |
| "SearchPage", | |
| "SearchPage.jsx" | |
| ); | |
| // On non-default-locale routes (e.g. /zh-CN/docs/...) the plugin strips only | |
| // `baseUrl` before matching `searchContextByPaths`, so the | |
| // remaining URI keeps the locale prefix and never matches `docs`. | |
| // Combined with `useAllContextsWithNoSearchContext: true`, this collapses the | |
| // search back to the root index (which contains every version, including the | |
| // retired v4.x). These two patches strip the non-default locale prefix in both | |
| // the runtime SearchBar and the build-time context partitioning so each locale | |
| // gets its own per-context index files and the dropdown stays scoped to the | |
| // active docs tree. | |
| function patchSearchBarContent(content) { | |
| if (content.includes("PATCH(locale-aware-context)")) { | |
| return content; | |
| } | |
| const needle = | |
| " if (location.pathname.startsWith(versionUrl)) {\n" + | |
| " const uri = location.pathname.substring(versionUrl.length);\n" + | |
| " let matchedPath;\n" + | |
| " for (const _path of searchContextByPaths) {\n" + | |
| " const path = typeof _path === \"string\" ? _path : _path.path;\n" + | |
| " if (uri === path || uri.startsWith(`${path}/`)) {\n" + | |
| " matchedPath = path;\n" + | |
| " break;\n" + | |
| " }\n" + | |
| " }\n"; | |
| const replacement = | |
| " if (location.pathname.startsWith(versionUrl)) {\n" + | |
| " const uri = location.pathname.substring(versionUrl.length);\n" + | |
| " // PATCH(locale-aware-context): strip non-default locale prefix so\n" + | |
| " // /zh-CN/docs/... match the configured contexts.\n" + | |
| " const __localePrefix = currentLocale + \"/\";\n" + | |
| " const __ctxUri = uri.startsWith(__localePrefix) ? uri.substring(__localePrefix.length) : uri;\n" + | |
| " let matchedPath;\n" + | |
| " for (const _path of searchContextByPaths) {\n" + | |
| " const path = typeof _path === \"string\" ? _path : _path.path;\n" + | |
| " if (__ctxUri === path || __ctxUri.startsWith(`${path}/`)) {\n" + | |
| " matchedPath = path;\n" + | |
| " break;\n" + | |
| " }\n" + | |
| " }\n"; | |
| if (!content.includes(needle)) { | |
| return null; | |
| } | |
| return content.replace(needle, replacement); | |
| } | |
| function patchPostBuildFactoryContent(content) { | |
| if (content.includes("PATCH(locale-aware-context)")) { | |
| return content; | |
| } | |
| const needle1 = | |
| " if (searchContextByPaths) {\n" + | |
| " const { baseUrl } = buildData;\n" + | |
| " const rootAllDocs = [];\n"; | |
| const replacement1 = | |
| " if (searchContextByPaths) {\n" + | |
| " const { baseUrl } = buildData;\n" + | |
| " // PATCH(locale-aware-context): strip non-default locale prefix so\n" + | |
| " // /zh-CN/docs/... gets assigned to the configured\n" + | |
| " // contexts instead of falling into the root bucket.\n" + | |
| " const __i18n = buildData.i18n;\n" + | |
| " const __localePrefix = __i18n && __i18n.currentLocale && __i18n.currentLocale !== __i18n.defaultLocale\n" + | |
| " ? __i18n.currentLocale + \"/\"\n" + | |
| " : \"\";\n" + | |
| " const rootAllDocs = [];\n"; | |
| const needle2 = | |
| " if (doc.u.startsWith(baseUrl)) {\n" + | |
| " const uri = doc.u.substring(baseUrl.length);\n" + | |
| " const matchedPaths = [];\n" + | |
| " for (const _path of searchContextByPaths) {\n" + | |
| " const path = typeof _path === \"string\" ? _path : _path.path;\n" + | |
| " if (uri === path || uri.startsWith(`${path}/`)) {\n" + | |
| " matchedPaths.push(path);\n" + | |
| " }\n" + | |
| " }\n"; | |
| const replacement2 = | |
| " if (doc.u.startsWith(baseUrl)) {\n" + | |
| " const uri = doc.u.substring(baseUrl.length);\n" + | |
| " const __ctxUri = __localePrefix && uri.startsWith(__localePrefix)\n" + | |
| " ? uri.substring(__localePrefix.length)\n" + | |
| " : uri;\n" + | |
| " const matchedPaths = [];\n" + | |
| " for (const _path of searchContextByPaths) {\n" + | |
| " const path = typeof _path === \"string\" ? _path : _path.path;\n" + | |
| " if (__ctxUri === path || __ctxUri.startsWith(`${path}/`)) {\n" + | |
| " matchedPaths.push(path);\n" + | |
| " }\n" + | |
| " }\n"; | |
| if (!content.includes(needle1) || !content.includes(needle2)) { | |
| return null; | |
| } | |
| return content.replace(needle1, replacement1).replace(needle2, replacement2); | |
| } | |
| // The search worker fetches `${versionUrl}search-index-*.json`. `versionUrl` | |
| // defaults to `siteConfig.baseUrl` (= "/"), which is locale-agnostic, so on | |
| // /zh-CN/* pages the worker still hits the default-locale index files at the | |
| // site root and never sees the per-locale indexes that Docusaurus emits | |
| // under build/{locale}/. These two patches make `versionUrl` carry the active | |
| // locale prefix in both the SearchBar (live dropdown + `?version=` it writes | |
| // into the "see all" link) and the SearchPage (direct loads of /{locale}/search | |
| // with no explicit `?version=`). | |
| function patchSearchBarVersionUrlContent(content) { | |
| if (content.includes("PATCH(locale-aware-version-url)")) { | |
| return content; | |
| } | |
| const needle1 = | |
| " const { siteConfig: { baseUrl }, i18n: { currentLocale }, } = useDocusaurusContext();\n"; | |
| const replacement1 = | |
| " const { siteConfig: { baseUrl }, i18n: { currentLocale, defaultLocale }, } = useDocusaurusContext();\n"; | |
| const needle2 = | |
| " }\n" + | |
| " }\n" + | |
| " const history = useHistory();\n"; | |
| const replacement2 = | |
| " }\n" + | |
| " }\n" + | |
| " // PATCH(locale-aware-version-url): non-default locales serve their own\n" + | |
| " // search-index files under /{locale}/. Prepend the locale to versionUrl\n" + | |
| " // so the worker fetches /{locale}/search-index-*.json instead of the EN one.\n" + | |
| " if (currentLocale && defaultLocale && currentLocale !== defaultLocale) {\n" + | |
| " const __localeSegment = currentLocale + \"/\";\n" + | |
| " if (!versionUrl.startsWith(baseUrl + __localeSegment)) {\n" + | |
| " versionUrl = baseUrl + __localeSegment + versionUrl.substring(baseUrl.length);\n" + | |
| " }\n" + | |
| " }\n" + | |
| " const history = useHistory();\n"; | |
| if (!content.includes(needle1) || !content.includes(needle2)) { | |
| return null; | |
| } | |
| return content.replace(needle1, replacement1).replace(needle2, replacement2); | |
| } | |
| function patchSearchPageVersionUrlContent(content) { | |
| if (content.includes("PATCH(locale-aware-version-url)")) { | |
| return content; | |
| } | |
| const needle1 = | |
| " const { siteConfig: { baseUrl }, i18n: { currentLocale }, } = useDocusaurusContext();\n"; | |
| const replacement1 = | |
| " const { siteConfig: { baseUrl }, i18n: { currentLocale, defaultLocale }, } = useDocusaurusContext();\n"; | |
| const needle2 = " const versionUrl = `${baseUrl}${searchVersion}`;\n"; | |
| const replacement2 = | |
| " // PATCH(locale-aware-version-url): when there is no explicit ?version=\n" + | |
| " // and the page is rendered under a non-default locale, fall back to that\n" + | |
| " // locale's index files instead of the default-locale root.\n" + | |
| " const versionUrl = searchVersion\n" + | |
| " ? `${baseUrl}${searchVersion}`\n" + | |
| " : (currentLocale && defaultLocale && currentLocale !== defaultLocale\n" + | |
| " ? `${baseUrl}${currentLocale}/`\n" + | |
| " : baseUrl);\n"; | |
| if (!content.includes(needle1) || !content.includes(needle2)) { | |
| return null; | |
| } | |
| return content.replace(needle1, replacement1).replace(needle2, replacement2); | |
| } | |
| function patchOneFile(filePath, patchFn, label) { | |
| if (!fs.existsSync(filePath)) { | |
| console.warn(`[patch-search-tokenize] skip(${label}): file not found: ${filePath}`); | |
| return; | |
| } | |
| const original = fs.readFileSync(filePath, "utf8"); | |
| const patched = patchFn(original); | |
| if (patched == null) { | |
| console.warn( | |
| `[patch-search-tokenize] skip(${label}): target block not found (plugin content may have changed)` | |
| ); | |
| return; | |
| } | |
| if (patched === original) { | |
| console.log(`[patch-search-tokenize] already patched (${label})`); | |
| return; | |
| } | |
| fs.writeFileSync(filePath, patched, "utf8"); | |
| console.log(`[patch-search-tokenize] patched (${label})`); | |
| } | |
| function main() { | |
| patchOneFile(targetSearchBarFile, patchSearchBarContent, "client-searchBar-locale"); | |
| patchOneFile( | |
| targetPostBuildFactoryFile, | |
| patchPostBuildFactoryContent, | |
| "server-postBuildFactory-locale" | |
| ); | |
| patchOneFile( | |
| targetSearchBarFile, | |
| patchSearchBarVersionUrlContent, | |
| "client-searchBar-versionUrl" | |
| ); | |
| patchOneFile( | |
| targetSearchPageFile, | |
| patchSearchPageVersionUrlContent, | |
| "client-searchPage-versionUrl" | |
| ); | |
| } | |
| main(); |