Skip to content

Commit 964f84a

Browse files
authored
Merge pull request #530 from easyops-cn/copilot/fix-529
fix: Fix non-deterministic search-index.json generation
2 parents 13de464 + 3d2ac4e commit 964f84a

File tree

4 files changed

+84
-71
lines changed

4 files changed

+84
-71
lines changed

docusaurus-search-local/src/server/utils/postBuildFactory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export function postBuildFactory(
8282
docsByDirMap.set("", allDocuments);
8383
}
8484

85-
for (const [k, allDocs] of docsByDirMap) {
85+
for (const [k, allDocs] of Array.from(docsByDirMap.entries()).sort(([a], [b]) => a.localeCompare(b))) {
8686
const searchIndex = buildIndex(allDocs, config);
8787

8888
debugInfo(`writing index (/${k}) to disk`);

docusaurus-search-local/src/server/utils/processDocInfos.spec.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@ describe("processDocInfos", () => {
4949
{
5050
outDir: "/build",
5151
paths: [
52-
{
53-
filePath: expect.toMatchPath("/build/docs/a/index.html"),
54-
type: "docs",
55-
url: "/base/docs/a",
56-
},
5752
{
5853
filePath: expect.toMatchPath("/build/blog/b/index.html"),
5954
type: "blog",
6055
url: "/base/blog/b",
6156
},
57+
{
58+
filePath: expect.toMatchPath("/build/docs/a/index.html"),
59+
type: "docs",
60+
url: "/base/docs/a",
61+
},
6262
{
6363
filePath: expect.toMatchPath("/build/page/index.html"),
6464
type: "page",
@@ -119,16 +119,16 @@ describe("processDocInfos", () => {
119119
{
120120
outDir: "/build",
121121
paths: [
122-
{
123-
filePath: expect.toMatchPath("/build/docs/a.html"),
124-
type: "docs",
125-
url: "/base/docs/a",
126-
},
127122
{
128123
filePath: expect.toMatchPath("/build/blog/b.html"),
129124
type: "blog",
130125
url: "/base/blog/b",
131126
},
127+
{
128+
filePath: expect.toMatchPath("/build/docs/a.html"),
129+
type: "docs",
130+
url: "/base/docs/a",
131+
},
132132
{
133133
filePath: expect.toMatchPath("/build/page.html"),
134134
type: "page",
@@ -189,16 +189,16 @@ describe("processDocInfos", () => {
189189
{
190190
outDir: "/build",
191191
paths: [
192-
{
193-
filePath: expect.toMatchPath("/build/docs/a/index.html"),
194-
type: "docs",
195-
url: "/base/docs/a/",
196-
},
197192
{
198193
filePath: expect.toMatchPath("/build/blog/b/index.html"),
199194
type: "blog",
200195
url: "/base/blog/b/",
201196
},
197+
{
198+
filePath: expect.toMatchPath("/build/docs/a/index.html"),
199+
type: "docs",
200+
url: "/base/docs/a/",
201+
},
202202
{
203203
filePath: expect.toMatchPath("/build/page/index.html"),
204204
type: "page",

docusaurus-search-local/src/server/utils/processDocInfos.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,11 @@ export function processDocInfos(
5959

6060
// Create a list of files to index per document version. This will always include all pages and blogs.
6161
const result = [];
62-
for (const [versionOutDir, docs] of versionData.entries()) {
63-
const versionPaths = routesPaths
62+
for (const [versionOutDir, docs] of Array.from(versionData.entries()).sort(([a], [b]) => a.localeCompare(b))) {
63+
// Sort routesPaths to ensure deterministic processing order
64+
const sortedRoutesPaths = routesPaths.slice().sort();
65+
66+
const versionPaths = sortedRoutesPaths
6467
.map<DocInfoWithRoute | undefined>((url: string) => {
6568
// istanbul ignore next
6669
if (!url.startsWith(baseUrl)) {

docusaurus-search-local/src/server/utils/scanDocuments.ts

Lines changed: 63 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ export async function scanDocuments(
3333
contentDocuments,
3434
];
3535

36-
await Promise.all(
37-
DocInfoWithFilePathList.map(async ({ filePath, url, type }) => {
36+
// Process documents in parallel but assign IDs deterministically based on input order
37+
const processedDocs = await Promise.all(
38+
DocInfoWithFilePathList.map(async ({ filePath, url, type }, index) => {
3839
debugVerbose(
3940
`parsing %s file %o of %o`,
4041
type,
@@ -47,73 +48,82 @@ export async function scanDocuments(
4748
const parsed = parse(html, type, url, config);
4849
if (!parsed) {
4950
// Unlisted content
50-
return;
51+
return null;
5152
}
52-
const { pageTitle, description, keywords, sections, breadcrumb } = parsed;
53+
return { parsed, url, index };
54+
})
55+
);
5356

54-
const titleId = getNextDocId();
57+
// Process results in input order to ensure deterministic ID assignment
58+
for (let i = 0; i < processedDocs.length; i++) {
59+
const result = processedDocs[i];
60+
if (!result) continue;
61+
62+
const { parsed, url } = result;
63+
const { pageTitle, description, keywords, sections, breadcrumb } = parsed;
64+
65+
const titleId = getNextDocId();
66+
67+
titleDocuments.push({
68+
i: titleId,
69+
t: pageTitle,
70+
u: url,
71+
b: breadcrumb,
72+
});
73+
74+
if (description) {
75+
descriptionDocuments.push({
76+
i: titleId,
77+
t: description,
78+
s: pageTitle,
79+
u: url,
80+
p: titleId,
81+
});
82+
}
5583

56-
titleDocuments.push({
84+
if (keywords) {
85+
keywordsDocuments.push({
5786
i: titleId,
58-
t: pageTitle,
87+
t: keywords,
88+
s: pageTitle,
5989
u: url,
60-
b: breadcrumb,
90+
p: titleId,
6191
});
92+
}
6293

63-
if (description) {
64-
descriptionDocuments.push({
65-
i: titleId,
66-
t: description,
67-
s: pageTitle,
68-
u: url,
69-
p: titleId,
70-
});
71-
}
94+
for (const section of sections) {
95+
const trimmedHash = getTrimmedHash(section.hash, url);
7296

73-
if (keywords) {
74-
keywordsDocuments.push({
75-
i: titleId,
76-
t: keywords,
77-
s: pageTitle,
97+
if (section.title !== pageTitle) {
98+
if (trimmedHash === false) {
99+
continue;
100+
}
101+
102+
headingDocuments.push({
103+
i: getNextDocId(),
104+
t: section.title,
78105
u: url,
106+
h: trimmedHash,
79107
p: titleId,
80108
});
81109
}
82110

83-
for (const section of sections) {
84-
const trimmedHash = getTrimmedHash(section.hash, url);
85-
86-
if (section.title !== pageTitle) {
87-
if (trimmedHash === false) {
88-
continue;
89-
}
90-
91-
headingDocuments.push({
92-
i: getNextDocId(),
93-
t: section.title,
94-
u: url,
95-
h: trimmedHash,
96-
p: titleId,
97-
});
111+
if (section.content) {
112+
if (trimmedHash === false) {
113+
continue;
98114
}
99115

100-
if (section.content) {
101-
if (trimmedHash === false) {
102-
continue;
103-
}
104-
105-
contentDocuments.push({
106-
i: getNextDocId(),
107-
t: section.content,
108-
s: section.title || pageTitle,
109-
u: url,
110-
h: trimmedHash,
111-
p: titleId,
112-
});
113-
}
116+
contentDocuments.push({
117+
i: getNextDocId(),
118+
t: section.content,
119+
s: section.title || pageTitle,
120+
u: url,
121+
h: trimmedHash,
122+
p: titleId,
123+
});
114124
}
115-
})
116-
);
125+
}
126+
}
117127
return allDocuments;
118128
}
119129

0 commit comments

Comments
 (0)