@mintlify/common 1.0.1023 → 1.0.1024
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/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/navigation/decoratedNavigationConfig.d.ts +2 -0
- package/dist/navigation/decoratedNavigationConfig.js +204 -0
- package/dist/navigation/filterDivisions.d.ts +65 -0
- package/dist/navigation/filterDivisions.js +697 -0
- package/dist/navigation/filterDivisions.test.d.ts +1 -0
- package/dist/navigation/filterDivisions.test.js +670 -0
- package/dist/navigation/getScopedNavForPath.test.d.ts +1 -0
- package/dist/navigation/getScopedNavForPath.test.js +135 -0
- package/dist/navigation/getVersionOrLanguageFromPath.d.ts +12 -0
- package/dist/navigation/getVersionOrLanguageFromPath.js +71 -0
- package/dist/navigation/getVersionOrLanguageFromPath.test.d.ts +1 -0
- package/dist/navigation/getVersionOrLanguageFromPath.test.js +133 -0
- package/dist/navigation/index.d.ts +5 -0
- package/dist/navigation/index.js +5 -0
- package/dist/navigation/isCommonPage.d.ts +8 -0
- package/dist/navigation/isCommonPage.js +36 -0
- package/dist/navigation/isCommonPage.test.d.ts +1 -0
- package/dist/navigation/isCommonPage.test.js +56 -0
- package/dist/navigation/pathsDifferByOneSegment.test.d.ts +1 -0
- package/dist/navigation/pathsDifferByOneSegment.test.js +91 -0
- package/dist/navigation/scopeNavToPath.d.ts +18 -0
- package/dist/navigation/scopeNavToPath.js +455 -0
- package/dist/navigation/scopeNavToPath.test.d.ts +1 -0
- package/dist/navigation/scopeNavToPath.test.js +117 -0
- package/dist/navigation/scopeOffPathDivisions.test.d.ts +1 -0
- package/dist/navigation/scopeOffPathDivisions.test.js +852 -0
- package/dist/navigation/scopeProductNav.test.d.ts +1 -0
- package/dist/navigation/scopeProductNav.test.js +288 -0
- package/dist/navigation/updateFirstHrefInDivision.d.ts +29 -0
- package/dist/navigation/updateFirstHrefInDivision.js +383 -0
- package/dist/paths/isEqualIgnoringLeadingSlash.d.ts +1 -0
- package/dist/paths/isEqualIgnoringLeadingSlash.js +21 -0
- package/dist/paths/isEqualIgnoringLeadingSlash.test.d.ts +1 -0
- package/dist/paths/isEqualIgnoringLeadingSlash.test.js +43 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import { locales } from '@mintlify/models';
|
|
2
|
+
import { divisions, } from '@mintlify/validation';
|
|
3
|
+
import { readObjectArrayProperty } from '../divisions/iterateObjectArrayProperty.js';
|
|
4
|
+
import { isEqualIgnoringLeadingSlash } from '../paths/isEqualIgnoringLeadingSlash.js';
|
|
5
|
+
import { isPage } from './isPage.js';
|
|
6
|
+
export var DivisionMatchLevel;
|
|
7
|
+
(function (DivisionMatchLevel) {
|
|
8
|
+
DivisionMatchLevel[DivisionMatchLevel["EXACT"] = 0] = "EXACT";
|
|
9
|
+
DivisionMatchLevel[DivisionMatchLevel["PATH"] = 1] = "PATH";
|
|
10
|
+
DivisionMatchLevel[DivisionMatchLevel["GROUP"] = 2] = "GROUP";
|
|
11
|
+
DivisionMatchLevel[DivisionMatchLevel["DIVISION"] = 3] = "DIVISION";
|
|
12
|
+
DivisionMatchLevel[DivisionMatchLevel["NONE"] = 4] = "NONE";
|
|
13
|
+
})(DivisionMatchLevel || (DivisionMatchLevel = {}));
|
|
14
|
+
const ACCEPTED_BASE_PATHS = ['docs', 'doc', 'documentation', 'help'];
|
|
15
|
+
export function updateFirstHrefInDivision({ type, currentPath, entry, parentGroups, nearestDivisionValue, isActiveGroup, inActiveDivision, currentDivisionValue, allDivisionValues, firstHrefInDivision, navigationDivisions, isHiddenPage, activeGroupNames, }) {
|
|
16
|
+
if (typeof entry !== 'object')
|
|
17
|
+
return;
|
|
18
|
+
if (isPage(entry)) {
|
|
19
|
+
maybeUpdateFirstHrefInDivision(type, entry.href, nearestDivisionValue, isActiveGroup, inActiveDivision, currentPath, currentDivisionValue, allDivisionValues, firstHrefInDivision, isHiddenPage);
|
|
20
|
+
}
|
|
21
|
+
if ('pages' in entry && Array.isArray(entry.pages)) {
|
|
22
|
+
const root = 'root' in entry && entry.root != null && typeof entry.root === 'object'
|
|
23
|
+
? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- root is a DecoratedNavigationPage
|
|
24
|
+
entry.root
|
|
25
|
+
: undefined;
|
|
26
|
+
const isRootActive = root !== undefined &&
|
|
27
|
+
isPage(root) &&
|
|
28
|
+
isEqualIgnoringLeadingSlash(root.href, currentPath) &&
|
|
29
|
+
currentDivisionValue === nearestDivisionValue;
|
|
30
|
+
const isActivePages = entry.pages.some((subEntry) => isPage(subEntry) && isEqualIgnoringLeadingSlash(subEntry.href, currentPath)) && currentDivisionValue === nearestDivisionValue;
|
|
31
|
+
if (root !== undefined) {
|
|
32
|
+
updateFirstHrefInDivision({
|
|
33
|
+
type,
|
|
34
|
+
currentPath,
|
|
35
|
+
entry: root,
|
|
36
|
+
parentGroups: parentGroups.length ? parentGroups : entry.pages,
|
|
37
|
+
nearestDivisionValue,
|
|
38
|
+
isActiveGroup: isActiveGroup || isActivePages || isRootActive,
|
|
39
|
+
inActiveDivision,
|
|
40
|
+
currentDivisionValue,
|
|
41
|
+
allDivisionValues,
|
|
42
|
+
firstHrefInDivision,
|
|
43
|
+
navigationDivisions,
|
|
44
|
+
isHiddenPage,
|
|
45
|
+
activeGroupNames,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
for (const page of entry.pages) {
|
|
49
|
+
if (typeof page === 'object') {
|
|
50
|
+
updateFirstHrefInDivision({
|
|
51
|
+
type,
|
|
52
|
+
currentPath,
|
|
53
|
+
entry: page,
|
|
54
|
+
parentGroups: parentGroups.length ? parentGroups : entry.pages,
|
|
55
|
+
nearestDivisionValue,
|
|
56
|
+
isActiveGroup: isActiveGroup || isActivePages || isRootActive,
|
|
57
|
+
inActiveDivision,
|
|
58
|
+
currentDivisionValue,
|
|
59
|
+
allDivisionValues,
|
|
60
|
+
firstHrefInDivision,
|
|
61
|
+
navigationDivisions,
|
|
62
|
+
isHiddenPage,
|
|
63
|
+
activeGroupNames,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if ('groups' in entry && !isPage(entry)) {
|
|
69
|
+
for (const group of entry.groups) {
|
|
70
|
+
if (typeof group !== 'object')
|
|
71
|
+
continue;
|
|
72
|
+
const groupName = 'group' in group && typeof group.group === 'string' ? group.group : undefined;
|
|
73
|
+
const matchesCurrentDivisionGroup = checkActiveGroup(group, currentPath) && currentDivisionValue === nearestDivisionValue;
|
|
74
|
+
const matchesActiveSiblingGroup = type === 'version' &&
|
|
75
|
+
currentDivisionValue !== nearestDivisionValue &&
|
|
76
|
+
!!groupName &&
|
|
77
|
+
(activeGroupNames === null || activeGroupNames === void 0 ? void 0 : activeGroupNames.has(groupName)) === true;
|
|
78
|
+
const isActiveGroup = matchesCurrentDivisionGroup || matchesActiveSiblingGroup;
|
|
79
|
+
updateFirstHrefInDivision({
|
|
80
|
+
type,
|
|
81
|
+
currentPath,
|
|
82
|
+
entry: group,
|
|
83
|
+
parentGroups: entry.groups,
|
|
84
|
+
nearestDivisionValue,
|
|
85
|
+
isActiveGroup,
|
|
86
|
+
inActiveDivision,
|
|
87
|
+
currentDivisionValue,
|
|
88
|
+
allDivisionValues,
|
|
89
|
+
firstHrefInDivision,
|
|
90
|
+
navigationDivisions,
|
|
91
|
+
isHiddenPage,
|
|
92
|
+
activeGroupNames,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const key of [...divisions, 'groups']) {
|
|
97
|
+
const subDivisions = readObjectArrayProperty(entry, key);
|
|
98
|
+
if (!subDivisions)
|
|
99
|
+
continue;
|
|
100
|
+
let inActiveDivision = false;
|
|
101
|
+
if (allDivisionValues && allDivisionValues.length) {
|
|
102
|
+
if (type === 'version' && key === 'versions') {
|
|
103
|
+
const localDivisionValueCount = navigationDivisions.versions.filter((version) => !version.isGlobal).length;
|
|
104
|
+
inActiveDivision =
|
|
105
|
+
subDivisions.length === localDivisionValueCount &&
|
|
106
|
+
subDivisions.every((subDivision) =>
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
108
|
+
allDivisionValues.includes(subDivision.version));
|
|
109
|
+
}
|
|
110
|
+
else if (type === 'language' && key === 'languages') {
|
|
111
|
+
const localDivisionValueCount = navigationDivisions.languages.filter((language) => !language.isGlobal).length;
|
|
112
|
+
inActiveDivision =
|
|
113
|
+
subDivisions.length === localDivisionValueCount &&
|
|
114
|
+
subDivisions.every((subDivision) =>
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
116
|
+
allDivisionValues.includes(subDivision.language));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
let activeGroupsInVersionContext = activeGroupNames;
|
|
120
|
+
if (type === 'version' && key === 'versions' && currentDivisionValue) {
|
|
121
|
+
const currentVersionDivision = subDivisions.find((subDivision) => 'version' in subDivision &&
|
|
122
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
123
|
+
subDivision.version === currentDivisionValue);
|
|
124
|
+
if (currentVersionDivision) {
|
|
125
|
+
activeGroupsInVersionContext = findActiveGroupNames(
|
|
126
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
127
|
+
currentVersionDivision, currentPath);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
for (const subDivision of subDivisions) {
|
|
131
|
+
let divisionValue;
|
|
132
|
+
if (type === 'version' && key === 'versions') {
|
|
133
|
+
divisionValue =
|
|
134
|
+
'version' in subDivision
|
|
135
|
+
? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
136
|
+
subDivision.version
|
|
137
|
+
: undefined;
|
|
138
|
+
}
|
|
139
|
+
else if (type === 'language' && key === 'languages') {
|
|
140
|
+
divisionValue =
|
|
141
|
+
'language' in subDivision
|
|
142
|
+
? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
143
|
+
subDivision.language
|
|
144
|
+
: undefined;
|
|
145
|
+
}
|
|
146
|
+
updateFirstHrefInDivision({
|
|
147
|
+
type,
|
|
148
|
+
currentPath,
|
|
149
|
+
entry: subDivision,
|
|
150
|
+
parentGroups: key === 'groups' && 'groups' in entry
|
|
151
|
+
? // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
152
|
+
entry.groups
|
|
153
|
+
: parentGroups,
|
|
154
|
+
nearestDivisionValue: divisionValue !== null && divisionValue !== void 0 ? divisionValue : nearestDivisionValue,
|
|
155
|
+
isActiveGroup,
|
|
156
|
+
inActiveDivision: inActiveDivision,
|
|
157
|
+
currentDivisionValue,
|
|
158
|
+
allDivisionValues,
|
|
159
|
+
firstHrefInDivision,
|
|
160
|
+
navigationDivisions,
|
|
161
|
+
isHiddenPage,
|
|
162
|
+
activeGroupNames: activeGroupsInVersionContext,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function findActiveGroupNames(entry, currentPath, activeGroupNames = new Set()) {
|
|
168
|
+
if (typeof entry !== 'object')
|
|
169
|
+
return activeGroupNames;
|
|
170
|
+
if ('groups' in entry && !isPage(entry)) {
|
|
171
|
+
for (const group of entry.groups) {
|
|
172
|
+
if (typeof group !== 'object')
|
|
173
|
+
continue;
|
|
174
|
+
if ('group' in group &&
|
|
175
|
+
typeof group.group === 'string' &&
|
|
176
|
+
checkActiveGroup(group, currentPath)) {
|
|
177
|
+
activeGroupNames.add(group.group);
|
|
178
|
+
}
|
|
179
|
+
findActiveGroupNames(group, currentPath, activeGroupNames);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if ('pages' in entry && Array.isArray(entry.pages)) {
|
|
183
|
+
if ('root' in entry && typeof entry.root === 'object') {
|
|
184
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
185
|
+
findActiveGroupNames(entry.root, currentPath, activeGroupNames);
|
|
186
|
+
}
|
|
187
|
+
for (const page of entry.pages) {
|
|
188
|
+
if (typeof page === 'object') {
|
|
189
|
+
findActiveGroupNames(page, currentPath, activeGroupNames);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
for (const key of divisions) {
|
|
194
|
+
const subDivisions = readObjectArrayProperty(entry, key);
|
|
195
|
+
if (!subDivisions)
|
|
196
|
+
continue;
|
|
197
|
+
for (const subDivision of subDivisions) {
|
|
198
|
+
findActiveGroupNames(subDivision, currentPath, activeGroupNames);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return activeGroupNames;
|
|
202
|
+
}
|
|
203
|
+
function parseLanguagePath(segments) {
|
|
204
|
+
let index = 0;
|
|
205
|
+
let locale;
|
|
206
|
+
let hasDocsBase = false;
|
|
207
|
+
const firstSegment = segments[0];
|
|
208
|
+
const firstIsDocsBase = !!firstSegment && ACCEPTED_BASE_PATHS.includes(firstSegment);
|
|
209
|
+
const firstIsLocale = !!firstSegment &&
|
|
210
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
211
|
+
locales.includes(firstSegment);
|
|
212
|
+
if (firstIsDocsBase) {
|
|
213
|
+
hasDocsBase = true;
|
|
214
|
+
index = 1;
|
|
215
|
+
const maybeLocale = segments[index];
|
|
216
|
+
if (maybeLocale &&
|
|
217
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
218
|
+
locales.includes(maybeLocale)) {
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
220
|
+
locale = maybeLocale;
|
|
221
|
+
index++;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
else if (firstIsLocale) {
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- TODO: Please fix this violation when you can!
|
|
226
|
+
locale = firstSegment;
|
|
227
|
+
index = 1;
|
|
228
|
+
const maybeDocsBase = segments[index];
|
|
229
|
+
if (maybeDocsBase && ACCEPTED_BASE_PATHS.includes(maybeDocsBase)) {
|
|
230
|
+
hasDocsBase = true;
|
|
231
|
+
index++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
let contentSegments = segments.slice(index);
|
|
235
|
+
if (contentSegments.at(-1) === 'index') {
|
|
236
|
+
contentSegments = contentSegments.slice(0, -1);
|
|
237
|
+
}
|
|
238
|
+
return { hasDocsBase, locale, contentSegments };
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* This function attempts to maintain some context when switching versions by following these rules:
|
|
242
|
+
* 1. stay on the active page, if it is in the new version (unversioned)
|
|
243
|
+
* 2. navigate to the first page that is in the new version AND in the active group, if one exists
|
|
244
|
+
* 3. navigate to the first page that is in the new version
|
|
245
|
+
*
|
|
246
|
+
* @param href The href of the page being processed
|
|
247
|
+
* @param finalVersion The version of the page being processed
|
|
248
|
+
* @param inActiveGroup Whether the page being processed is in the same group as the active page
|
|
249
|
+
* @param currentPath The path of the active page
|
|
250
|
+
* @param currentVersion The version of the active page
|
|
251
|
+
* @param versionStrings If defined, the array of version strings
|
|
252
|
+
* @param firstHrefInDivision A map from version strings to the href that should be navigated to when switching to that version
|
|
253
|
+
*/
|
|
254
|
+
function maybeUpdateFirstHrefInDivision(type, href, finalVersion, inActiveGroup, inActiveDivision, currentPath, currentVersion, versionStrings, firstHrefInDivision, isHiddenPage) {
|
|
255
|
+
const versionsToUpdate = finalVersion !== undefined ? [finalVersion] : (versionStrings !== null && versionStrings !== void 0 ? versionStrings : []);
|
|
256
|
+
// now that we support index.mdx, the href might empty string
|
|
257
|
+
// router.push cannot take empty string as url
|
|
258
|
+
href = href || '/';
|
|
259
|
+
// always use the current page if the current page is unversioned
|
|
260
|
+
if (href === currentPath) {
|
|
261
|
+
for (const version of versionsToUpdate) {
|
|
262
|
+
firstHrefInDivision.set(version, { href, matchLevel: DivisionMatchLevel.EXACT });
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// if the page is hidden, assign the current path to the current version with EXACT match level
|
|
267
|
+
if (isHiddenPage && currentVersion !== undefined) {
|
|
268
|
+
firstHrefInDivision.set(currentVersion, {
|
|
269
|
+
href: currentPath,
|
|
270
|
+
matchLevel: DivisionMatchLevel.EXACT,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
// if currentPath and href differ by exactly one path segment, and it's not the last,
|
|
274
|
+
// we make the BOLD assumption that href is a different version of the same page
|
|
275
|
+
if (currentVersion !== undefined && finalVersion !== undefined) {
|
|
276
|
+
const existingRef = firstHrefInDivision.get(finalVersion);
|
|
277
|
+
if ((existingRef === undefined || DivisionMatchLevel.PATH < existingRef.matchLevel) &&
|
|
278
|
+
pathsDifferByOneSegment(href, currentPath, type)) {
|
|
279
|
+
firstHrefInDivision.set(finalVersion, { href, matchLevel: DivisionMatchLevel.PATH });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (inActiveGroup) {
|
|
283
|
+
for (const version of versionsToUpdate) {
|
|
284
|
+
const existingHref = firstHrefInDivision.get(version);
|
|
285
|
+
if (existingHref === undefined || DivisionMatchLevel.GROUP < existingHref.matchLevel) {
|
|
286
|
+
firstHrefInDivision.set(version, { href, matchLevel: DivisionMatchLevel.GROUP });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
if (inActiveDivision) {
|
|
292
|
+
for (const version of versionsToUpdate) {
|
|
293
|
+
const existingHref = firstHrefInDivision.get(version);
|
|
294
|
+
if (existingHref === undefined || DivisionMatchLevel.DIVISION < existingHref.matchLevel) {
|
|
295
|
+
firstHrefInDivision.set(version, { href, matchLevel: DivisionMatchLevel.DIVISION });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
for (const version of versionsToUpdate) {
|
|
301
|
+
if (!firstHrefInDivision.has(version)) {
|
|
302
|
+
firstHrefInDivision.set(version, { href, matchLevel: DivisionMatchLevel.NONE });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
export function pathsDifferByOneSegment(a, b, type) {
|
|
307
|
+
const aSegments = a.split('/').filter(Boolean);
|
|
308
|
+
const bSegments = b.split('/').filter(Boolean);
|
|
309
|
+
// For language switching, we need to be more strict - paths should be identical except for the language segment
|
|
310
|
+
// once normalized, content segments should be identical and only locale prefix may differ.
|
|
311
|
+
if (type === 'language') {
|
|
312
|
+
const aPath = parseLanguagePath(aSegments);
|
|
313
|
+
const bPath = parseLanguagePath(bSegments);
|
|
314
|
+
if (aPath.hasDocsBase !== bPath.hasDocsBase)
|
|
315
|
+
return false;
|
|
316
|
+
// For language switching, the content parts (including version segments) must be identical.
|
|
317
|
+
if (aPath.contentSegments.length !== bPath.contentSegments.length)
|
|
318
|
+
return false;
|
|
319
|
+
for (let i = 0; i < aPath.contentSegments.length; i++) {
|
|
320
|
+
if (aPath.contentSegments[i] !== bPath.contentSegments[i]) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
// The paths are language variants if locale presence/value differs.
|
|
325
|
+
if (!aPath.locale && !bPath.locale)
|
|
326
|
+
return false;
|
|
327
|
+
return aPath.locale !== bPath.locale;
|
|
328
|
+
}
|
|
329
|
+
const aStartsWithDocs = aSegments[0] && ACCEPTED_BASE_PATHS.includes(aSegments[0]);
|
|
330
|
+
const bStartsWithDocs = bSegments[0] && ACCEPTED_BASE_PATHS.includes(bSegments[0]);
|
|
331
|
+
// Only compare like-for-like paths (both with docs base prefix or both without)
|
|
332
|
+
if (aStartsWithDocs !== bStartsWithDocs)
|
|
333
|
+
return false;
|
|
334
|
+
const aContentSegments = aStartsWithDocs ? aSegments.slice(1) : aSegments;
|
|
335
|
+
const bContentSegments = bStartsWithDocs ? bSegments.slice(1) : bSegments;
|
|
336
|
+
// Existing behavior: same-length paths are version variants if exactly one non-terminal segment differs.
|
|
337
|
+
if (aContentSegments.length === bContentSegments.length &&
|
|
338
|
+
aContentSegments.length >= 2 &&
|
|
339
|
+
aContentSegments.at(-1) === bContentSegments.at(-1)) {
|
|
340
|
+
let mismatchFound = false;
|
|
341
|
+
for (let i = 0; i < aContentSegments.length - 1; i++) {
|
|
342
|
+
if (aContentSegments[i] !== bContentSegments[i]) {
|
|
343
|
+
if (mismatchFound)
|
|
344
|
+
return false;
|
|
345
|
+
mismatchFound = true;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return mismatchFound;
|
|
349
|
+
}
|
|
350
|
+
// New behavior: handle root-vs-prefixed version paths by allowing one leading segment difference.
|
|
351
|
+
const longerContentSegments = aContentSegments.length > bContentSegments.length ? aContentSegments : bContentSegments;
|
|
352
|
+
const shorterContentSegments = aContentSegments.length > bContentSegments.length ? bContentSegments : aContentSegments;
|
|
353
|
+
if (longerContentSegments.length !== shorterContentSegments.length + 1)
|
|
354
|
+
return false;
|
|
355
|
+
if (shorterContentSegments.length === 0)
|
|
356
|
+
return false;
|
|
357
|
+
for (let i = 0; i < shorterContentSegments.length; i++) {
|
|
358
|
+
if (longerContentSegments[i + 1] !== shorterContentSegments[i]) {
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return true;
|
|
363
|
+
}
|
|
364
|
+
function checkActiveGroup(group, currentPath) {
|
|
365
|
+
if (isPage(group)) {
|
|
366
|
+
return isEqualIgnoringLeadingSlash(group.href, currentPath);
|
|
367
|
+
}
|
|
368
|
+
if ('pages' in group) {
|
|
369
|
+
if ('root' in group && typeof group.root === 'object') {
|
|
370
|
+
const isActivePage = checkActiveGroup(group.root, currentPath);
|
|
371
|
+
if (isActivePage) {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
for (const page of group.pages) {
|
|
376
|
+
const isActivePage = checkActiveGroup(page, currentPath);
|
|
377
|
+
if (isActivePage) {
|
|
378
|
+
return true;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
return false;
|
|
383
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isEqualIgnoringLeadingSlash(path1: string | undefined, path2: string | undefined): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { optionallyRemoveLeadingSlash, optionallyRemoveTrailingSlash, } from '../fs/optionallySlash.js';
|
|
2
|
+
import { replaceSlashIndex } from '../slug/replaceSlashIndex.js';
|
|
3
|
+
function normalizeIndexPath(path) {
|
|
4
|
+
if (path === '')
|
|
5
|
+
return 'index';
|
|
6
|
+
if (path === 'index')
|
|
7
|
+
return path;
|
|
8
|
+
if (path.endsWith('/index')) {
|
|
9
|
+
const normalized = replaceSlashIndex(path);
|
|
10
|
+
return normalized === '' ? 'index' : normalized;
|
|
11
|
+
}
|
|
12
|
+
return path;
|
|
13
|
+
}
|
|
14
|
+
export function isEqualIgnoringLeadingSlash(path1, path2) {
|
|
15
|
+
if (path1 == null || path2 == null || typeof path1 !== 'string' || typeof path2 !== 'string') {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const first = optionallyRemoveTrailingSlash(optionallyRemoveLeadingSlash(path1));
|
|
19
|
+
const second = optionallyRemoveTrailingSlash(optionallyRemoveLeadingSlash(path2));
|
|
20
|
+
return normalizeIndexPath(first) === normalizeIndexPath(second);
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { isEqualIgnoringLeadingSlash } from './isEqualIgnoringLeadingSlash.js';
|
|
2
|
+
describe('isEqualIgnoringLeadingSlash', () => {
|
|
3
|
+
test('false when non-string inputs are passed', () => {
|
|
4
|
+
expect(isEqualIgnoringLeadingSlash({}, {})).toEqual(false);
|
|
5
|
+
expect(isEqualIgnoringLeadingSlash(undefined, '')).toEqual(false);
|
|
6
|
+
expect(isEqualIgnoringLeadingSlash(0, null)).toEqual(false);
|
|
7
|
+
});
|
|
8
|
+
test('true when strings match ignoring leading slash', () => {
|
|
9
|
+
expect(isEqualIgnoringLeadingSlash('', '/')).toEqual(true);
|
|
10
|
+
expect(isEqualIgnoringLeadingSlash('/path/to', 'path/to')).toEqual(true);
|
|
11
|
+
});
|
|
12
|
+
test('handles index paths', () => {
|
|
13
|
+
expect(isEqualIgnoringLeadingSlash('', 'index')).toEqual(true);
|
|
14
|
+
expect(isEqualIgnoringLeadingSlash('/', 'index')).toEqual(true);
|
|
15
|
+
expect(isEqualIgnoringLeadingSlash('path/to/index', 'path/to')).toEqual(true);
|
|
16
|
+
expect(isEqualIgnoringLeadingSlash('/path/to/index', 'path/to')).toEqual(true);
|
|
17
|
+
expect(isEqualIgnoringLeadingSlash('path/to/index', '/path/to')).toEqual(true);
|
|
18
|
+
expect(isEqualIgnoringLeadingSlash('/index', '')).toEqual(true);
|
|
19
|
+
expect(isEqualIgnoringLeadingSlash('/index', '/')).toEqual(true);
|
|
20
|
+
});
|
|
21
|
+
test('handles complex index paths', () => {
|
|
22
|
+
expect(isEqualIgnoringLeadingSlash('/api/v1/index', '/api/v1')).toEqual(true);
|
|
23
|
+
expect(isEqualIgnoringLeadingSlash('indexing/guide', 'ing/guide')).toEqual(false);
|
|
24
|
+
expect(isEqualIgnoringLeadingSlash('reindex/data', 'rein/data')).toEqual(false);
|
|
25
|
+
expect(isEqualIgnoringLeadingSlash('index/subpath/index', 'index/subpath')).toEqual(true);
|
|
26
|
+
expect(isEqualIgnoringLeadingSlash('index/subpath', 'index/subpath/index')).toEqual(true);
|
|
27
|
+
expect(isEqualIgnoringLeadingSlash('/', 'index')).toEqual(true);
|
|
28
|
+
expect(isEqualIgnoringLeadingSlash('/index/index', 'index')).toEqual(true);
|
|
29
|
+
});
|
|
30
|
+
test('normal paths work', () => {
|
|
31
|
+
expect(isEqualIgnoringLeadingSlash('path/to/resource', 'path/to/different')).toEqual(false);
|
|
32
|
+
expect(isEqualIgnoringLeadingSlash('/a/b/c', '/x/y/z')).toEqual(false);
|
|
33
|
+
expect(isEqualIgnoringLeadingSlash('Path/To', 'path/to')).toEqual(false);
|
|
34
|
+
expect(isEqualIgnoringLeadingSlash('INDEX', 'index')).toEqual(false);
|
|
35
|
+
expect(isEqualIgnoringLeadingSlash('path/to', 'path/to')).toEqual(true);
|
|
36
|
+
});
|
|
37
|
+
test('weird cases work', () => {
|
|
38
|
+
expect(isEqualIgnoringLeadingSlash('', 'index')).toEqual(true);
|
|
39
|
+
expect(isEqualIgnoringLeadingSlash('/index', '/index')).toEqual(true);
|
|
40
|
+
expect(isEqualIgnoringLeadingSlash('/index', 'index')).toEqual(true);
|
|
41
|
+
expect(isEqualIgnoringLeadingSlash('/index', '')).toEqual(true);
|
|
42
|
+
});
|
|
43
|
+
});
|