@thallylabs/migrate 0.2.26 → 0.2.27
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/README.md +47 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +2299 -1524
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,15 +9,15 @@ import {
|
|
|
9
9
|
openSync,
|
|
10
10
|
readFileSync as readFileSync6,
|
|
11
11
|
readSync,
|
|
12
|
-
readdirSync as
|
|
13
|
-
realpathSync as
|
|
12
|
+
readdirSync as readdirSync4,
|
|
13
|
+
realpathSync as realpathSync4,
|
|
14
14
|
rmSync as rmSync2,
|
|
15
15
|
statSync
|
|
16
16
|
} from "fs";
|
|
17
17
|
import { createRequire } from "module";
|
|
18
18
|
import { basename as basename3, dirname as dirname4, extname as extname6, isAbsolute as isAbsolute3, posix as posix4, relative as relative6, resolve as resolvePath, sep as sep3 } from "path";
|
|
19
19
|
import { parse as parseYaml6 } from "yaml";
|
|
20
|
-
import * as
|
|
20
|
+
import * as ts5 from "typescript";
|
|
21
21
|
|
|
22
22
|
// src/inline-extraction.ts
|
|
23
23
|
import ts from "typescript";
|
|
@@ -808,6 +808,7 @@ import { unified } from "unified";
|
|
|
808
808
|
import { extname } from "path";
|
|
809
809
|
|
|
810
810
|
// src/path.ts
|
|
811
|
+
import { realpathSync } from "fs";
|
|
811
812
|
import { isAbsolute, relative, resolve, sep } from "path";
|
|
812
813
|
var SAFE_SEGMENT = /[^a-z0-9._-]+/gi;
|
|
813
814
|
function trimTrailingSlashes(value) {
|
|
@@ -829,7 +830,8 @@ function slugifySegment(value, preserveCase = false) {
|
|
|
829
830
|
} catch {
|
|
830
831
|
}
|
|
831
832
|
const normalized = preserveCase ? decoded : decoded.toLowerCase();
|
|
832
|
-
|
|
833
|
+
const slug2 = normalized.replace(/\.(?:html?|mdx?)$/i, "").replace(SAFE_SEGMENT, "-").replace(/(^-|-$)/g, "");
|
|
834
|
+
return slug2 === "." || slug2 === ".." ? "" : slug2;
|
|
833
835
|
}
|
|
834
836
|
function pageIdFromReference(value, preserveCase = false) {
|
|
835
837
|
const withoutQuery = value.split(/[?#]/, 1)[0].replace(/\\/g, "/").replace(/^\/+/, "");
|
|
@@ -884,6 +886,22 @@ function normalizeAssetPath(value) {
|
|
|
884
886
|
if (segments.some((segment) => segment === "." || segment === "..")) return null;
|
|
885
887
|
return segments.join("/");
|
|
886
888
|
}
|
|
889
|
+
function withinRealRoot(candidate, repositoryRoot) {
|
|
890
|
+
let real;
|
|
891
|
+
try {
|
|
892
|
+
real = realpathSync(candidate);
|
|
893
|
+
} catch {
|
|
894
|
+
return candidate;
|
|
895
|
+
}
|
|
896
|
+
const realRoot = realpathSync(repositoryRoot);
|
|
897
|
+
if (real !== realRoot && !real.startsWith(realRoot.endsWith(sep) ? realRoot : realRoot + sep)) {
|
|
898
|
+
throw new Error(`Migration path escapes its root: ${candidate}`);
|
|
899
|
+
}
|
|
900
|
+
return candidate;
|
|
901
|
+
}
|
|
902
|
+
function isPathEscapeError(error) {
|
|
903
|
+
return error instanceof Error && /escapes its root|Unsafe migration path/.test(error.message);
|
|
904
|
+
}
|
|
887
905
|
|
|
888
906
|
// src/mintlify-extras.ts
|
|
889
907
|
function objectValue(value) {
|
|
@@ -1048,7 +1066,7 @@ function playgroundDisplay(value, where, warn) {
|
|
|
1048
1066
|
warn?.(`${where} "auth" requires reader sign-in, which Thally does not support. Migrated as "simple" (no Try it). Set it to "interactive" to allow everyone.`);
|
|
1049
1067
|
return "simple";
|
|
1050
1068
|
}
|
|
1051
|
-
warn?.(`${where}
|
|
1069
|
+
warn?.(`${where} is not one of interactive, simple, none, auth and was dropped.`);
|
|
1052
1070
|
return void 0;
|
|
1053
1071
|
}
|
|
1054
1072
|
|
|
@@ -1059,13 +1077,23 @@ function titleFromId(id) {
|
|
|
1059
1077
|
return value.split(/[-_]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
1060
1078
|
}
|
|
1061
1079
|
var descriptionParser = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
|
|
1062
|
-
function
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1080
|
+
function plainText(node) {
|
|
1081
|
+
if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
|
|
1082
|
+
if (node.type === "image") return node.alt ?? "";
|
|
1083
|
+
if (node.type === "break") return " ";
|
|
1084
|
+
return (node.children ?? []).map(plainText).join("");
|
|
1085
|
+
}
|
|
1086
|
+
function mdxVisibleText(content, definitions) {
|
|
1087
|
+
const references = [...content.matchAll(/\[([^\]\n]+)\]/g)].map((match) => definitions?.get(match[1].trim().replace(/\s+/g, " ").toLowerCase()) ?? "").join("\n");
|
|
1088
|
+
try {
|
|
1089
|
+
return plainText(descriptionParser.parse(`# ${content}
|
|
1090
|
+
|
|
1091
|
+
${references}`)).trim();
|
|
1092
|
+
} catch {
|
|
1093
|
+
return content;
|
|
1068
1094
|
}
|
|
1095
|
+
}
|
|
1096
|
+
function firstParagraph(content) {
|
|
1069
1097
|
function findParagraph(node) {
|
|
1070
1098
|
if (node.type === "paragraph") {
|
|
1071
1099
|
const value = plainText(node).replace(/\s+/g, " ").trim();
|
|
@@ -1089,6 +1117,97 @@ function docusaurusAdmonitionTag(kind) {
|
|
|
1089
1117
|
if (kind === "caution" || kind === "warning") return "Warning";
|
|
1090
1118
|
return "Note";
|
|
1091
1119
|
}
|
|
1120
|
+
function scanJsxOpeningTag(source, start, name) {
|
|
1121
|
+
const opener = `<${name}`;
|
|
1122
|
+
let quote = null;
|
|
1123
|
+
let depth = 0;
|
|
1124
|
+
let braceQuote = null;
|
|
1125
|
+
let lineComment = false;
|
|
1126
|
+
let blockComment = false;
|
|
1127
|
+
const attributesStart = start + opener.length;
|
|
1128
|
+
for (let index = attributesStart; index < source.length; index++) {
|
|
1129
|
+
const char = source[index];
|
|
1130
|
+
if (char === "<" && !quote && !braceQuote && depth === 0 && !lineComment && !blockComment && source.startsWith(opener, index) && !/[\w$.-]/.test(source[index + opener.length] ?? "")) {
|
|
1131
|
+
return { attributes: source.slice(attributesStart, index), end: null, next: index };
|
|
1132
|
+
}
|
|
1133
|
+
if (lineComment) {
|
|
1134
|
+
if (char === "\n" || char === "\r") lineComment = false;
|
|
1135
|
+
} else if (blockComment) {
|
|
1136
|
+
if (char === "*" && source[index + 1] === "/") {
|
|
1137
|
+
blockComment = false;
|
|
1138
|
+
index++;
|
|
1139
|
+
}
|
|
1140
|
+
} else if (braceQuote) {
|
|
1141
|
+
if (char === "\\") index++;
|
|
1142
|
+
else if (char === braceQuote) braceQuote = null;
|
|
1143
|
+
} else if (depth > 0) {
|
|
1144
|
+
if (char === "/" && source[index + 1] === "/") {
|
|
1145
|
+
lineComment = true;
|
|
1146
|
+
index++;
|
|
1147
|
+
} else if (char === "/" && source[index + 1] === "*") {
|
|
1148
|
+
blockComment = true;
|
|
1149
|
+
index++;
|
|
1150
|
+
} else if (char === '"' || char === "'" || char === "`") braceQuote = char;
|
|
1151
|
+
else if (char === "{") depth++;
|
|
1152
|
+
else if (char === "}") depth--;
|
|
1153
|
+
} else if (quote) {
|
|
1154
|
+
if (char === quote) quote = null;
|
|
1155
|
+
} else if (char === '"' || char === "'") quote = char;
|
|
1156
|
+
else if (char === "{") depth++;
|
|
1157
|
+
else if (char === ">") return { attributes: source.slice(attributesStart, index), end: index, next: index + 1 };
|
|
1158
|
+
}
|
|
1159
|
+
return { attributes: source.slice(attributesStart), end: null, next: source.length };
|
|
1160
|
+
}
|
|
1161
|
+
function normalizeDocusaurusAdmonitionTags(segment) {
|
|
1162
|
+
const open = [];
|
|
1163
|
+
const marker = /<\/Admonition>|<Admonition(?![\w$.-])/g;
|
|
1164
|
+
let output = "";
|
|
1165
|
+
let cursor = 0;
|
|
1166
|
+
for (let match = marker.exec(segment); match; match = marker.exec(segment)) {
|
|
1167
|
+
if (match[0] === "</Admonition>") {
|
|
1168
|
+
output += `${segment.slice(cursor, match.index)}
|
|
1169
|
+
</${open.pop() ?? "Note"}>`;
|
|
1170
|
+
cursor = marker.lastIndex;
|
|
1171
|
+
continue;
|
|
1172
|
+
}
|
|
1173
|
+
const tag = scanJsxOpeningTag(segment, match.index, "Admonition");
|
|
1174
|
+
marker.lastIndex = tag.next;
|
|
1175
|
+
if (tag.end === null) continue;
|
|
1176
|
+
const type = tag.attributes.match(/\btype=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean)?.toLowerCase() ?? "note";
|
|
1177
|
+
const title = tag.attributes.match(/\btitle=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean)?.trim();
|
|
1178
|
+
const callout = docusaurusAdmonitionTag(type);
|
|
1179
|
+
open.push(callout);
|
|
1180
|
+
output += `${segment.slice(cursor, match.index)}${title ? `<${callout}>
|
|
1181
|
+
**${title}**
|
|
1182
|
+
` : `<${callout}>
|
|
1183
|
+
`}`;
|
|
1184
|
+
cursor = tag.end + 1;
|
|
1185
|
+
marker.lastIndex = cursor;
|
|
1186
|
+
}
|
|
1187
|
+
return output + segment.slice(cursor);
|
|
1188
|
+
}
|
|
1189
|
+
function normalizeDocusaurusLinkTags(segment) {
|
|
1190
|
+
const marker = /<Link(?![\w$.-])/g;
|
|
1191
|
+
let output = "";
|
|
1192
|
+
let cursor = 0;
|
|
1193
|
+
for (let match = marker.exec(segment); match; match = marker.exec(segment)) {
|
|
1194
|
+
const tag = scanJsxOpeningTag(segment, match.index, "Link");
|
|
1195
|
+
marker.lastIndex = tag.next;
|
|
1196
|
+
if (tag.end === null) continue;
|
|
1197
|
+
output += `${segment.slice(cursor, match.index)}<a${tag.attributes.replace(/(^|\s)to=/, "$1href=")}>`;
|
|
1198
|
+
cursor = tag.end + 1;
|
|
1199
|
+
marker.lastIndex = cursor;
|
|
1200
|
+
}
|
|
1201
|
+
return (output + segment.slice(cursor)).replace(/<\/Link>/g, "</a>");
|
|
1202
|
+
}
|
|
1203
|
+
function normalizeDocusaurusThemedImages(segment) {
|
|
1204
|
+
return segment.replace(/<ThemedImage\b([^<]*?)\/>/g, (original, attributes) => {
|
|
1205
|
+
const light = attributes.match(/\blight\s*:\s*(?:useBaseUrl\(\s*)?(?:"([^"]+)"|'([^']+)')/)?.slice(1).find(Boolean);
|
|
1206
|
+
if (!light) return original;
|
|
1207
|
+
const alt = attributes.match(/\balt=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find((value) => value !== void 0);
|
|
1208
|
+
return `<img src="${light.replace(/"/g, """)}"${alt === void 0 ? "" : ` alt="${alt.replace(/"/g, """)}"`} />`;
|
|
1209
|
+
});
|
|
1210
|
+
}
|
|
1092
1211
|
function normalizeDocusaurusAdmonitions(body) {
|
|
1093
1212
|
const lines = body.split("\n");
|
|
1094
1213
|
const openAdmonitions = [];
|
|
@@ -1260,10 +1379,51 @@ var SAFE_BUILTIN_ROOTS = /* @__PURE__ */ new Set([
|
|
|
1260
1379
|
"BigInt",
|
|
1261
1380
|
"Reflect"
|
|
1262
1381
|
]);
|
|
1382
|
+
function bindSnippetProps(body, properties) {
|
|
1383
|
+
if (!body.includes("props") || properties.size === 0) return body;
|
|
1384
|
+
let tree;
|
|
1385
|
+
try {
|
|
1386
|
+
tree = descriptionParser.parse(body);
|
|
1387
|
+
} catch {
|
|
1388
|
+
return body;
|
|
1389
|
+
}
|
|
1390
|
+
const edits = [];
|
|
1391
|
+
const inspectExpression = (value) => {
|
|
1392
|
+
if (!value || typeof value !== "object") return;
|
|
1393
|
+
const node = value;
|
|
1394
|
+
if (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "FunctionDeclaration") {
|
|
1395
|
+
const names = /* @__PURE__ */ new Set();
|
|
1396
|
+
node.params.forEach((param) => collectPatternNames(param, names));
|
|
1397
|
+
if (names.has("props")) return;
|
|
1398
|
+
}
|
|
1399
|
+
if (node.type === "MemberExpression" && node.object.type === "Identifier" && node.object.name === "props") {
|
|
1400
|
+
const key = !node.computed && node.property.type === "Identifier" ? node.property.name : node.computed && node.property.type === "Literal" && typeof node.property.value === "string" ? node.property.value : void 0;
|
|
1401
|
+
const replacement = key === void 0 ? void 0 : properties.get(key);
|
|
1402
|
+
if (replacement !== void 0 && body.slice(node.start, node.end).startsWith("props")) {
|
|
1403
|
+
edits.push({ start: node.start, end: node.end, value: JSON.stringify(replacement) });
|
|
1404
|
+
return;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
for (const child of Object.values(value)) {
|
|
1408
|
+
if (Array.isArray(child)) child.forEach(inspectExpression);
|
|
1409
|
+
else if (child && typeof child === "object") inspectExpression(child);
|
|
1410
|
+
}
|
|
1411
|
+
};
|
|
1412
|
+
const visit = (value) => {
|
|
1413
|
+
if (!value || typeof value !== "object") return;
|
|
1414
|
+
const node = value;
|
|
1415
|
+
if (node.data?.estree) inspectExpression(node.data.estree);
|
|
1416
|
+
for (const attribute of node.attributes ?? []) visit(attribute.value);
|
|
1417
|
+
for (const child of node.children ?? []) visit(child);
|
|
1418
|
+
};
|
|
1419
|
+
visit(tree);
|
|
1420
|
+
return edits.sort((a, b) => b.start - a.start).reduce((text2, edit) => text2.slice(0, edit.start) + edit.value + text2.slice(edit.end), body);
|
|
1421
|
+
}
|
|
1263
1422
|
function leadingPageHeading(body) {
|
|
1423
|
+
if (!/^ {0,3}(?:#(?:[ \t]|$)|=+[ \t\r]*$)/m.test(body)) return void 0;
|
|
1264
1424
|
try {
|
|
1265
1425
|
const root = descriptionParser.parse(body);
|
|
1266
|
-
const first = root.children?.find((node) => node.type !== "mdxjsEsm" && node.type !== "html");
|
|
1426
|
+
const first = root.children?.find((node) => node.type !== "mdxjsEsm" && node.type !== "html" && !(node.type === "mdxFlowExpression" && node.data?.estree?.body.length === 0));
|
|
1267
1427
|
if (first?.type !== "heading" || first.depth !== 1) return void 0;
|
|
1268
1428
|
const text2 = (node) => node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" ? "" : node.value ?? (node.children ?? []).map(text2).join("");
|
|
1269
1429
|
const title = text2(first).replace(/\s+/g, " ").trim();
|
|
@@ -1274,6 +1434,12 @@ function leadingPageHeading(body) {
|
|
|
1274
1434
|
return void 0;
|
|
1275
1435
|
}
|
|
1276
1436
|
}
|
|
1437
|
+
function resolvePageDisplayHeading(page, platform) {
|
|
1438
|
+
const heading = leadingPageHeading(page.body);
|
|
1439
|
+
if (!heading || heading.title !== page.title && platform !== "docusaurus") return;
|
|
1440
|
+
if (heading.title !== page.title) page.headingTitle = heading.title;
|
|
1441
|
+
page.body = `${page.body.slice(0, heading.start)}${page.body.slice(heading.end)}`.trim();
|
|
1442
|
+
}
|
|
1277
1443
|
function collectPatternNames(pattern, names) {
|
|
1278
1444
|
switch (pattern.type) {
|
|
1279
1445
|
case "Identifier":
|
|
@@ -2045,13 +2211,13 @@ function normalizeMintlifyUpdateLabels(body) {
|
|
|
2045
2211
|
} catch {
|
|
2046
2212
|
return match;
|
|
2047
2213
|
}
|
|
2048
|
-
const
|
|
2214
|
+
const plainText2 = (node) => {
|
|
2049
2215
|
if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
|
|
2050
2216
|
if (node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" || node.type === "mdxjsEsm") return null;
|
|
2051
|
-
const parts = (node.children ?? []).map(
|
|
2217
|
+
const parts = (node.children ?? []).map(plainText2);
|
|
2052
2218
|
return parts.some((part) => part === null) ? null : parts.join("");
|
|
2053
2219
|
};
|
|
2054
|
-
const label =
|
|
2220
|
+
const label = plainText2(root)?.replace(/\s+/g, " ").trim();
|
|
2055
2221
|
return label && !/[{}]/.test(label) ? `${prefix}{${JSON.stringify(label)}}` : match;
|
|
2056
2222
|
});
|
|
2057
2223
|
}
|
|
@@ -2334,11 +2500,12 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
2334
2500
|
const runMintlify = platform === "mintlify";
|
|
2335
2501
|
const runDocusaurus = platform === "docusaurus";
|
|
2336
2502
|
const sourceBody = runDocusaurus && unwrapMdxCodeBlocks ? unwrapDocusaurusMdxCodeBlocks(body) : body;
|
|
2337
|
-
let rewritten =
|
|
2503
|
+
let rewritten = normalizeExplicitHeadingIds(
|
|
2338
2504
|
runFern ? normalizeFernFileTrees(normalizeFernCallouts(normalizeNestedCodeFences(sourceBody))) : normalizeNestedCodeFences(sourceBody),
|
|
2339
2505
|
void 0,
|
|
2340
2506
|
{ headingMarkers: runMintlify }
|
|
2341
|
-
)
|
|
2507
|
+
);
|
|
2508
|
+
if (runDocusaurus) rewritten = normalizeDocusaurusAdmonitions(rewritten);
|
|
2342
2509
|
if (runMintlify) rewritten = markMintlifyHeadings(rewritten);
|
|
2343
2510
|
if (runDocusaurus) {
|
|
2344
2511
|
rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
|
|
@@ -2352,6 +2519,7 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
2352
2519
|
return `${attribute}="/${path.replace(/^\.\//, "").replace(/^\/+/, "")}"`;
|
|
2353
2520
|
}
|
|
2354
2521
|
));
|
|
2522
|
+
rewritten = replaceOutsideCode(rewritten, (segment) => normalizeDocusaurusThemedImages(normalizeDocusaurusAdmonitionTags(segment)));
|
|
2355
2523
|
rewritten = normalizeDocusaurusTabBlocks(normalizeDocusaurusTabs(removeGlobalDocusaurusImports(rewritten)));
|
|
2356
2524
|
rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
|
|
2357
2525
|
/\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])(@site\/[^'"]+)\2\s*;?/g,
|
|
@@ -2366,10 +2534,11 @@ function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
|
2366
2534
|
rewritten = replaceOutsideCode(rewritten, (segment) => {
|
|
2367
2535
|
let result = convertHtmlStyleAttributes(segment).replace(/<!--([\s\S]*?)-->/g, (_match, content) => mdxComment(content)).replace(/<Danger(\s[^>]*)?>/g, "<Error$1>").replace(/<\/Danger>/g, "</Error>").replace(/<Warn(\s[^>]*)?>/g, "<Warning$1>").replace(/<\/Warn>/g, "</Warning>").replace(/<Check(\s[^>]*)?>/g, "<Note$1>").replace(/<\/Check>/g, "</Note>").replace(/<Tree\.Folder/g, "<Folder").replace(/<\/Tree\.Folder>/g, "</Folder>").replace(/<Tree\.File/g, "<File").replace(/<\/Tree\.File>/g, "</File>");
|
|
2368
2536
|
if (runDocusaurus) {
|
|
2369
|
-
result = result.replace(/<TabItem\b([
|
|
2537
|
+
result = result.replace(/<TabItem\b([^<>]*)>/g, (_match, attributes) => {
|
|
2370
2538
|
const title = attributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? attributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? "Tab";
|
|
2371
2539
|
return `<Tab title="${title.replace(/"/g, """)}">`;
|
|
2372
|
-
}).replace(/<\/TabItem>/g, "</Tab>")
|
|
2540
|
+
}).replace(/<\/TabItem>/g, "</Tab>");
|
|
2541
|
+
result = normalizeDocusaurusLinkTags(result).replace(/<(?:DocCardList|TOCInline)\b[^<>]*\/>/g, "");
|
|
2373
2542
|
}
|
|
2374
2543
|
if (runMintlify) {
|
|
2375
2544
|
result = normalizeMintlifyUpdateLabels(result).replace(/<FileTree(\s[^>]*)?>/g, "<Tree$1>").replace(/<\/FileTree>/g, "</Tree>").replace(/<GitHub\.Repo/g, "<GitHub").replace(/<\/GitHub\.Repo>/g, "</GitHub>").replace(/<Column(\s[^>]*)?>/g, "<div$1>").replace(/<\/Column>/g, "</div>");
|
|
@@ -2399,16 +2568,14 @@ function parseMarkdownPage(input) {
|
|
|
2399
2568
|
...input.locale ? { locale: input.locale } : {}
|
|
2400
2569
|
};
|
|
2401
2570
|
const identity = input.resolveIdentity?.(parsed.data, fallbackIdentity) ?? fallbackIdentity;
|
|
2402
|
-
|
|
2571
|
+
const body = removeUndefinedExpressions(injectReactHookImports(normalizeMdx(parsed.content, input.platform)), input.warn).trim();
|
|
2403
2572
|
const keywords = Array.isArray(parsed.data.keywords) ? parsed.data.keywords.filter((value) => typeof value === "string") : [];
|
|
2404
2573
|
const heading = leadingPageHeading(body);
|
|
2405
2574
|
const title = typeof parsed.data.title === "string" && parsed.data.title.trim() ? parsed.data.title.trim() : heading?.title ?? titleFromId(identity.navigationId);
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
}
|
|
2409
|
-
const navTitle = typeof parsed.data.sidebarTitle === "string" && parsed.data.sidebarTitle.trim() ? parsed.data.sidebarTitle.trim() : typeof parsed.data.navTitle === "string" && parsed.data.navTitle.trim() ? parsed.data.navTitle.trim() : void 0;
|
|
2575
|
+
const sidebarTitle = input.platform === "docusaurus" ? parsed.data.sidebar_label : parsed.data.sidebarTitle;
|
|
2576
|
+
const navTitle = typeof sidebarTitle === "string" && sidebarTitle.trim() ? sidebarTitle.trim() : typeof parsed.data.navTitle === "string" && parsed.data.navTitle.trim() ? parsed.data.navTitle.trim() : void 0;
|
|
2410
2577
|
const icon = typeof parsed.data.icon === "string" && parsed.data.icon.trim() ? parsed.data.icon.trim() : void 0;
|
|
2411
|
-
const iconType = icon && ["regular", "solid", "outline", "brands"].includes(
|
|
2578
|
+
const iconType = icon && typeof parsed.data.iconType === "string" && ["regular", "solid", "outline", "brands"].includes(parsed.data.iconType) ? parsed.data.iconType : void 0;
|
|
2412
2579
|
const meta = {};
|
|
2413
2580
|
for (const [source, field] of PAGE_META_FIELDS) {
|
|
2414
2581
|
const value = parsed.data[source];
|
|
@@ -2418,7 +2585,7 @@ function parseMarkdownPage(input) {
|
|
|
2418
2585
|
const sourceMode = typeof parsed.data.mode === "string" ? parsed.data.mode : void 0;
|
|
2419
2586
|
const mode = sourceMode === "frame" ? "wide" : ["default", "wide", "custom", "center", "home"].includes(sourceMode ?? "") ? sourceMode : void 0;
|
|
2420
2587
|
const description = typeof parsed.data.description === "string" && parsed.data.description.trim() ? parsed.data.description.trim() : typeof parsed.data.openapi === "string" && parsed.data.openapi.trim() ? "" : firstParagraph(body);
|
|
2421
|
-
|
|
2588
|
+
const page = {
|
|
2422
2589
|
id: identity.id,
|
|
2423
2590
|
navigationId: identity.navigationId,
|
|
2424
2591
|
locale: identity.locale,
|
|
@@ -2440,6 +2607,8 @@ function parseMarkdownPage(input) {
|
|
|
2440
2607
|
source: input.source,
|
|
2441
2608
|
...parsed.error ? { frontmatterError: parsed.error } : {}
|
|
2442
2609
|
};
|
|
2610
|
+
resolvePageDisplayHeading(page, input.platform);
|
|
2611
|
+
return page;
|
|
2443
2612
|
}
|
|
2444
2613
|
var PAGE_META_FIELDS = [
|
|
2445
2614
|
["og:title", "ogTitle"],
|
|
@@ -2463,7 +2632,7 @@ function apiFrontmatter(data, warn) {
|
|
|
2463
2632
|
if (data.authMethod !== void 0 && data.authMethod !== null) {
|
|
2464
2633
|
const method = typeof data.authMethod === "string" ? data.authMethod.trim().toLowerCase() : "";
|
|
2465
2634
|
if (AUTH_METHODS.has(method)) result.authMethod = method;
|
|
2466
|
-
else warn?.(`The page's "authMethod" frontmatter
|
|
2635
|
+
else warn?.(`The page's "authMethod" frontmatter is not one of bearer, basic, key, none and was dropped.`);
|
|
2467
2636
|
}
|
|
2468
2637
|
return result;
|
|
2469
2638
|
}
|
|
@@ -2499,6 +2668,10 @@ function normalizeIndentedFences(content) {
|
|
|
2499
2668
|
}
|
|
2500
2669
|
const match = line.match(/^([ \t]{4,})(`{3,}|~{3,})/);
|
|
2501
2670
|
if (match) {
|
|
2671
|
+
if (listIndent !== void 0 && match[1].length > listIndent) {
|
|
2672
|
+
ordinaryFence = match[2];
|
|
2673
|
+
return line;
|
|
2674
|
+
}
|
|
2502
2675
|
active = { indent: match[1], marker: match[2] };
|
|
2503
2676
|
return line.slice(match[1].length);
|
|
2504
2677
|
}
|
|
@@ -3065,7 +3238,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
3065
3238
|
function expandCandidates(candidate) {
|
|
3066
3239
|
const candidates = [candidate];
|
|
3067
3240
|
if (!extname2(candidate)) {
|
|
3068
|
-
candidates.push(...[".tsx", ".jsx", ".ts", ".js", ".mjs", ".json"].map((extension) => candidate + extension));
|
|
3241
|
+
candidates.push(...[".tsx", ".jsx", ".ts", ".js", ".mjs", ".json", ".d.ts"].map((extension) => candidate + extension));
|
|
3069
3242
|
candidates.push(...["index.tsx", "index.jsx", "index.ts", "index.js"].map((name) => resolve2(candidate, name)));
|
|
3070
3243
|
} else if (extname2(candidate) === ".js") {
|
|
3071
3244
|
candidates.push(candidate.slice(0, -3) + ".ts", candidate.slice(0, -3) + ".tsx");
|
|
@@ -3130,7 +3303,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
3130
3303
|
}
|
|
3131
3304
|
const text2 = content.toString("utf8");
|
|
3132
3305
|
const syntax = ts2.transpileModule(text2, {
|
|
3133
|
-
fileName: path,
|
|
3306
|
+
fileName: path.replace(/\.d\.ts$/, ".ts"),
|
|
3134
3307
|
reportDiagnostics: true,
|
|
3135
3308
|
compilerOptions: { jsx: ts2.JsxEmit.Preserve, target: ts2.ScriptTarget.ESNext, module: ts2.ModuleKind.ESNext }
|
|
3136
3309
|
});
|
|
@@ -3976,13 +4149,92 @@ function projectAuthoredStyles(css, contents) {
|
|
|
3976
4149
|
}
|
|
3977
4150
|
|
|
3978
4151
|
// src/docusaurus.ts
|
|
3979
|
-
import { existsSync as
|
|
3980
|
-
import { extname as
|
|
3981
|
-
import
|
|
4152
|
+
import { existsSync as existsSync4, lstatSync as lstatSync4, readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
|
|
4153
|
+
import { extname as extname5, posix as posix3 } from "path";
|
|
4154
|
+
import postcss3 from "postcss";
|
|
4155
|
+
|
|
4156
|
+
// src/css-colors.ts
|
|
4157
|
+
var channel = (value) => Number(value);
|
|
4158
|
+
function cssColorToHex(raw) {
|
|
4159
|
+
const value = raw.trim().replace(/\s*!important$/i, "");
|
|
4160
|
+
if (value.length > 64) return void 0;
|
|
4161
|
+
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(value)?.[1];
|
|
4162
|
+
if (hex) return `#${(hex.length === 3 ? [...hex].map((digit) => digit + digit).join("") : hex).toLowerCase()}`;
|
|
4163
|
+
const rgb = /^(?:rgba?\(\s*)?(\d{1,3})(?:\s*,\s*|\s+)(\d{1,3})(?:\s*,\s*|\s+)(\d{1,3})\s*(?:[,/]\s*[\d.]+%?\s*)?\)?$/i.exec(value);
|
|
4164
|
+
if (!rgb) return void 0;
|
|
4165
|
+
const channels = [rgb[1], rgb[2], rgb[3]].map(channel);
|
|
4166
|
+
if (channels.some((entry) => entry > 255)) return void 0;
|
|
4167
|
+
if (value.includes("(") !== value.includes(")")) return void 0;
|
|
4168
|
+
return `#${channels.map((entry) => entry.toString(16).padStart(2, "0")).join("")}`;
|
|
4169
|
+
}
|
|
4170
|
+
function stripComments(css) {
|
|
4171
|
+
let out = "";
|
|
4172
|
+
let index = 0;
|
|
4173
|
+
while (index < css.length) {
|
|
4174
|
+
const open = css.indexOf("/*", index);
|
|
4175
|
+
if (open < 0) break;
|
|
4176
|
+
out += css.slice(index, open);
|
|
4177
|
+
const close = css.indexOf("*/", open + 2);
|
|
4178
|
+
if (close < 0) return out;
|
|
4179
|
+
index = close + 2;
|
|
4180
|
+
}
|
|
4181
|
+
return out + css.slice(index);
|
|
4182
|
+
}
|
|
4183
|
+
function declarations(body, into) {
|
|
4184
|
+
for (const declaration of body.split(";")) {
|
|
4185
|
+
const colon = declaration.indexOf(":");
|
|
4186
|
+
if (colon < 0) continue;
|
|
4187
|
+
const name = declaration.slice(0, colon).trim().toLowerCase();
|
|
4188
|
+
if (name !== "--primary" && name !== "--primary-light" && name !== "--primary-dark") continue;
|
|
4189
|
+
const hex = cssColorToHex(declaration.slice(colon + 1));
|
|
4190
|
+
if (hex) into[name.slice(2)] = hex;
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
function cssBrandVars(css) {
|
|
4194
|
+
const source = stripComments(css);
|
|
4195
|
+
const root = {};
|
|
4196
|
+
const dark = {};
|
|
4197
|
+
let index = 0;
|
|
4198
|
+
while (index < source.length) {
|
|
4199
|
+
const open = source.indexOf("{", index);
|
|
4200
|
+
if (open < 0) break;
|
|
4201
|
+
let prelude = (source.slice(index, open).split("}").pop() ?? "").trim();
|
|
4202
|
+
while (prelude.startsWith("@") && prelude.includes(";")) prelude = prelude.slice(prelude.indexOf(";") + 1).trim();
|
|
4203
|
+
let depth = 1;
|
|
4204
|
+
let close = open + 1;
|
|
4205
|
+
for (; close < source.length && depth > 0; close++) {
|
|
4206
|
+
if (source[close] === "{") depth++;
|
|
4207
|
+
else if (source[close] === "}") depth--;
|
|
4208
|
+
}
|
|
4209
|
+
index = close;
|
|
4210
|
+
if (prelude.startsWith("@")) continue;
|
|
4211
|
+
for (const selector of prelude.split(",")) {
|
|
4212
|
+
const name = selector.trim().toLowerCase();
|
|
4213
|
+
const target = name === ":root" || name === "html" ? root : name === ".dark" || name === "html.dark" || name === ":root.dark" ? dark : void 0;
|
|
4214
|
+
if (target) declarations(source.slice(open + 1, close - 1), target);
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
return { root, dark };
|
|
4218
|
+
}
|
|
4219
|
+
function brandColorsFromVars({ root, dark }) {
|
|
4220
|
+
const result = {};
|
|
4221
|
+
const lightAccent = root.primary;
|
|
4222
|
+
const lightButton = root["primary-dark"] ?? root.primary;
|
|
4223
|
+
if (lightAccent || lightButton) result.light = { ...lightAccent ? { accent: lightAccent } : {}, ...lightButton ? { primary: lightButton } : {} };
|
|
4224
|
+
const darkAccent = dark["primary-light"] ?? dark.primary;
|
|
4225
|
+
const darkButton = dark["primary-dark"] ?? dark["primary-light"] ?? dark.primary;
|
|
4226
|
+
if (darkAccent || darkButton) result.dark = { ...darkAccent ? { accent: darkAccent } : {}, ...darkButton ? { primary: darkButton } : {} };
|
|
4227
|
+
return result.light || result.dark ? result : void 0;
|
|
4228
|
+
}
|
|
4229
|
+
|
|
4230
|
+
// src/fern.ts
|
|
4231
|
+
import { existsSync as existsSync3, lstatSync as lstatSync3, readFileSync as readFileSync3, readdirSync } from "fs";
|
|
4232
|
+
import { basename as basename2, dirname as dirname3, extname as extname4, relative as relative4 } from "path";
|
|
4233
|
+
import { load } from "cheerio";
|
|
3982
4234
|
import { parse as parseYaml2 } from "yaml";
|
|
3983
4235
|
|
|
3984
4236
|
// src/navigation.ts
|
|
3985
|
-
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
4237
|
+
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync as realpathSync2 } from "fs";
|
|
3986
4238
|
import { dirname as dirname2, extname as extname3, relative as relative3 } from "path";
|
|
3987
4239
|
var MAX_MINTLIFY_CONFIG_BYTES = 2e7;
|
|
3988
4240
|
function addMintlifyDirectoryRedirects(config, pages) {
|
|
@@ -4136,8 +4388,8 @@ function readMintlifyJson(path, repositoryRoot) {
|
|
|
4136
4388
|
if (!existsSync2(path) || !lstatSync2(path).isFile()) {
|
|
4137
4389
|
throw new Error(`Mintlify config reference is not a regular file: ${relative3(repositoryRoot, path)}`);
|
|
4138
4390
|
}
|
|
4139
|
-
const realPath =
|
|
4140
|
-
const realRoot =
|
|
4391
|
+
const realPath = realpathSync2(path);
|
|
4392
|
+
const realRoot = realpathSync2(repositoryRoot);
|
|
4141
4393
|
resolveWithin(realRoot, relative3(realRoot, realPath));
|
|
4142
4394
|
if (lstatSync2(realPath).size > MAX_MINTLIFY_CONFIG_BYTES) {
|
|
4143
4395
|
throw new Error(`Mintlify config reference exceeded ${MAX_MINTLIFY_CONFIG_BYTES / 1e6} MB: ${relative3(repositoryRoot, path)}`);
|
|
@@ -5221,1433 +5473,1695 @@ function pruneMissingNavigationPages(config, availableIds) {
|
|
|
5221
5473
|
return { ...config, tabs };
|
|
5222
5474
|
}
|
|
5223
5475
|
|
|
5224
|
-
// src/
|
|
5476
|
+
// src/fern.ts
|
|
5225
5477
|
var MAX_CONFIG_BYTES = 2e7;
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
"
|
|
5229
|
-
"
|
|
5230
|
-
|
|
5231
|
-
"
|
|
5232
|
-
|
|
5233
|
-
|
|
5478
|
+
function announcementMarkdown(message) {
|
|
5479
|
+
const document = load(message);
|
|
5480
|
+
document("script, style, iframe, object, embed, svg, math").remove();
|
|
5481
|
+
const escapeText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/([\\`*_\[\]{}()!])/g, "\\$1");
|
|
5482
|
+
const links = /* @__PURE__ */ new Map();
|
|
5483
|
+
document("a").each((index, element) => {
|
|
5484
|
+
const marker = `THALLYANNOUNCEMENTLINK${index}TOKEN`;
|
|
5485
|
+
const label = document(element).text().trim();
|
|
5486
|
+
const rawHref = document(element).attr("href")?.trim() ?? "";
|
|
5487
|
+
let href;
|
|
5488
|
+
if (/^https?:\/\//i.test(rawHref)) {
|
|
5489
|
+
try {
|
|
5490
|
+
href = new URL(rawHref).toString();
|
|
5491
|
+
} catch {
|
|
5492
|
+
}
|
|
5493
|
+
} else if (rawHref.startsWith("/") && !rawHref.startsWith("//") && !/[\\<>\s]/.test(rawHref)) {
|
|
5494
|
+
href = rawHref;
|
|
5495
|
+
}
|
|
5496
|
+
const destination = href?.replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
5497
|
+
links.set(marker, destination && label ? `[${escapeText(label)}](${destination})` : escapeText(label));
|
|
5498
|
+
document(element).replaceWith(marker);
|
|
5499
|
+
});
|
|
5500
|
+
let text2 = escapeText(document("body").text());
|
|
5501
|
+
for (const [marker, link] of links) text2 = text2.replaceAll(marker, link);
|
|
5502
|
+
return text2.trim();
|
|
5503
|
+
}
|
|
5234
5504
|
function objectValue3(value) {
|
|
5235
5505
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
5236
5506
|
}
|
|
5237
|
-
function
|
|
5238
|
-
return value.
|
|
5239
|
-
}
|
|
5240
|
-
function normalizeDocId(value) {
|
|
5241
|
-
return value.split(/[?#]/, 1)[0].replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.(?:mdx?)$/i, "").split("/").filter(Boolean).map(stripNumberPrefix).join("/");
|
|
5507
|
+
function titleCase2(value) {
|
|
5508
|
+
return value.split(/[-_]/).filter(Boolean).map((word) => ["api", "cli", "sdk", "ui"].includes(word.toLowerCase()) ? word.toUpperCase() : word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
5242
5509
|
}
|
|
5243
|
-
function
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
decoded = decodeURIComponent(value);
|
|
5247
|
-
} catch {
|
|
5510
|
+
function readBoundedYaml(path) {
|
|
5511
|
+
if (lstatSync3(path).size > MAX_CONFIG_BYTES) {
|
|
5512
|
+
throw new Error("The Fern config is larger than 20 MB and could not be imported.");
|
|
5248
5513
|
}
|
|
5249
|
-
return
|
|
5514
|
+
return parseYaml2(readFileSync3(path, "utf8"));
|
|
5250
5515
|
}
|
|
5251
|
-
function
|
|
5252
|
-
const
|
|
5253
|
-
|
|
5516
|
+
function readFernConfig(fernRoot) {
|
|
5517
|
+
const sourcePath = "docs.yml";
|
|
5518
|
+
const path = resolveWithin(fernRoot, sourcePath);
|
|
5519
|
+
if (!existsSync3(path) || !lstatSync3(path).isFile()) return null;
|
|
5520
|
+
const config = objectValue3(readBoundedYaml(path));
|
|
5521
|
+
if (!config) throw new Error("fern/docs.yml must contain an object.");
|
|
5522
|
+
return { config, sourcePath };
|
|
5254
5523
|
}
|
|
5255
|
-
function
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
return normalized.join("/") || "introduction";
|
|
5524
|
+
function warnOnce2(context, key, message) {
|
|
5525
|
+
if (context.warningKeys.has(key)) return;
|
|
5526
|
+
context.warningKeys.add(key);
|
|
5527
|
+
context.warnings.push({ code: "unsupported-config", message });
|
|
5260
5528
|
}
|
|
5261
|
-
function
|
|
5262
|
-
|
|
5529
|
+
function firstLeafId(nodes) {
|
|
5530
|
+
for (const node of nodes) {
|
|
5531
|
+
if (typeof node === "string") return node;
|
|
5532
|
+
const nested = firstLeafId(node.pages);
|
|
5533
|
+
if (nested) return nested;
|
|
5534
|
+
}
|
|
5535
|
+
return void 0;
|
|
5263
5536
|
}
|
|
5264
|
-
function
|
|
5265
|
-
|
|
5537
|
+
function addBareRouteRedirect(segments, pages, context) {
|
|
5538
|
+
if (segments.length === 0) return;
|
|
5539
|
+
const destination = firstLeafId(pages);
|
|
5540
|
+
const source = segments.join("/");
|
|
5541
|
+
if (destination && destination !== source) {
|
|
5542
|
+
context.bareRouteRedirects.push({ source: `/${source}`, destination: `/${destination}` });
|
|
5543
|
+
}
|
|
5266
5544
|
}
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
const fallbackDirectory = sourceDirectory ?? (fallback === "introduction" ? "" : posix3.dirname(fallback));
|
|
5271
|
-
const resolved = posix3.normalize(posix3.join(fallbackDirectory, slug2));
|
|
5272
|
-
if (resolved === ".." || resolved.startsWith("../")) return null;
|
|
5273
|
-
return docusaurusRouteId(resolved);
|
|
5545
|
+
var FERN_SEGMENT_ALIAS = /^[\w.-]+$/;
|
|
5546
|
+
function fernBasicSlug(value) {
|
|
5547
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
5274
5548
|
}
|
|
5275
|
-
function
|
|
5276
|
-
const
|
|
5277
|
-
|
|
5278
|
-
const configuredId = typeof frontmatter.id === "string" && frontmatter.id.trim() ? stripNumberPrefix(frontmatter.id.trim()) : null;
|
|
5279
|
-
const docId = configuredId ? normalizeDocId(sourceDirectory === "." ? configuredId : posix3.join(sourceDirectory, configuredId)) : sourceDocId;
|
|
5280
|
-
const defaultNavigationId = defaultDocusaurusRouteId(docId) ?? fallback.navigationId;
|
|
5281
|
-
const configuredSlug = typeof frontmatter.slug === "string" ? frontmatter.slug.trim() : "";
|
|
5282
|
-
const navigationId = configuredSlug ? routeFromSlug(
|
|
5283
|
-
configuredSlug,
|
|
5284
|
-
defaultNavigationId,
|
|
5285
|
-
sourceDirectory === "." ? "" : sourceDirectory
|
|
5286
|
-
) ?? defaultNavigationId : defaultNavigationId;
|
|
5287
|
-
const position = typeof frontmatter.sidebar_position === "number" && Number.isFinite(frontmatter.sidebar_position) ? frontmatter.sidebar_position : void 0;
|
|
5288
|
-
return {
|
|
5289
|
-
identity: { id: storageIdForRoute(navigationId), navigationId },
|
|
5290
|
-
descriptor: {
|
|
5291
|
-
sourcePath: sourcePath.replace(/\\/g, "/"),
|
|
5292
|
-
docId,
|
|
5293
|
-
navigationId,
|
|
5294
|
-
...position === void 0 ? {} : { sidebarPosition: position }
|
|
5295
|
-
}
|
|
5296
|
-
};
|
|
5297
|
-
}
|
|
5298
|
-
function sourceReferenceKey(value) {
|
|
5299
|
-
return value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.(?:mdx?)$/i, "").replace(/\/$/, "");
|
|
5549
|
+
function fernLabelSlug(label) {
|
|
5550
|
+
const decamelized = label.replace(/[()]/g, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2");
|
|
5551
|
+
return fernBasicSlug(decamelized);
|
|
5300
5552
|
}
|
|
5301
|
-
function
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
if (!fence) fence = marker[1];
|
|
5310
|
-
else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && /^\s*$/.test(line.slice(marker[0].length))) fence = null;
|
|
5311
|
-
continue;
|
|
5312
|
-
}
|
|
5313
|
-
if (fence) continue;
|
|
5314
|
-
const heading = line.match(/^ {0,3}(#{2,6})\s+(.+?)\s*#*\s*$/);
|
|
5315
|
-
if (!heading) continue;
|
|
5316
|
-
const preceding = lines[index - 1]?.match(/^<a id="([\p{L}\p{N}_.:$-]{1,120})"><\/a>$/u);
|
|
5317
|
-
const plainText = !/[<>{}\[\]`*_]/.test(heading[2]);
|
|
5318
|
-
const generatedId = plainText ? heading[2].normalize("NFC").toLowerCase().replace(/[^\p{L}\p{M}\p{N}]+/gu, "-").replace(/(^-|-$)/g, "") : null;
|
|
5319
|
-
headings.push({ index, level: heading[1].length, id: preceding?.[1] ?? generatedId });
|
|
5553
|
+
function segmentFor(value, label, context) {
|
|
5554
|
+
if (value["skip-slug"] === true) return null;
|
|
5555
|
+
if (typeof value.slug !== "string") return fernLabelSlug(label) || null;
|
|
5556
|
+
const explicit = value.slug.trim();
|
|
5557
|
+
if (!explicit) return null;
|
|
5558
|
+
const slugified = fernBasicSlug(explicit);
|
|
5559
|
+
if (context && explicit !== slugified && FERN_SEGMENT_ALIAS.test(explicit)) {
|
|
5560
|
+
context.segmentAliases.set(slugified, explicit);
|
|
5320
5561
|
}
|
|
5321
|
-
return
|
|
5562
|
+
return slugified || null;
|
|
5322
5563
|
}
|
|
5323
|
-
function
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
const
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5564
|
+
function uniqueNavigationId(base, context) {
|
|
5565
|
+
if (!context.seenNavigationIds.has(base)) return base;
|
|
5566
|
+
let index = 2;
|
|
5567
|
+
while (context.seenNavigationIds.has(`${base}-${index}`)) index++;
|
|
5568
|
+
return `${base}-${index}`;
|
|
5569
|
+
}
|
|
5570
|
+
function registerPageAt(rawPath, base, context, navTitle, hidden = false) {
|
|
5571
|
+
let sourcePath;
|
|
5572
|
+
try {
|
|
5573
|
+
const absolute = withinRealRoot(resolveWithinRoot(
|
|
5574
|
+
context.pathPrefix ? resolveWithinRoot(context.fernRoot, context.pathPrefix, context.repositoryRoot) : context.fernRoot,
|
|
5575
|
+
rawPath.trim(),
|
|
5576
|
+
context.repositoryRoot
|
|
5577
|
+
), context.repositoryRoot);
|
|
5578
|
+
sourcePath = relative4(context.fernRoot, absolute).replace(/\\/g, "/");
|
|
5579
|
+
} catch {
|
|
5580
|
+
warnOnce2(context, `fern-unsafe-page-${rawPath}`, `Skipped the Fern page "${rawPath}" because it points outside the repository (symlink or ..).`);
|
|
5581
|
+
return "";
|
|
5582
|
+
}
|
|
5583
|
+
const navigationId = uniqueNavigationId(base || "introduction", context);
|
|
5584
|
+
context.seenNavigationIds.add(navigationId);
|
|
5585
|
+
const isHidden = hidden || context.hiddenInherited === true;
|
|
5586
|
+
context.descriptors.push({ sourcePath, navigationId, ...navTitle ? { navTitle } : {}, ...isHidden ? { hidden: true } : {} });
|
|
5587
|
+
const kept = context.sourcePathToNavigationId.get(sourcePath);
|
|
5588
|
+
if (kept) {
|
|
5589
|
+
if (!isHidden) {
|
|
5590
|
+
const owner = context.descriptors.find((descriptor) => descriptor.navigationId === kept);
|
|
5591
|
+
if (owner) owner.hidden = false;
|
|
5347
5592
|
}
|
|
5348
|
-
|
|
5349
|
-
const alias = aliases.get(index);
|
|
5350
|
-
return alias ? [`<a id="${alias}"></a>`, line] : [line];
|
|
5351
|
-
}).join("\n");
|
|
5593
|
+
return kept;
|
|
5352
5594
|
}
|
|
5595
|
+
context.sourcePathToNavigationId.set(sourcePath, navigationId);
|
|
5596
|
+
return navigationId;
|
|
5353
5597
|
}
|
|
5354
|
-
function
|
|
5355
|
-
const
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5598
|
+
function registerFolder(object, parentSegments, context) {
|
|
5599
|
+
const rawPath = String(object.folder).trim();
|
|
5600
|
+
let folder;
|
|
5601
|
+
try {
|
|
5602
|
+
folder = withinRealRoot(resolveWithinRoot(
|
|
5603
|
+
context.pathPrefix ? resolveWithinRoot(context.fernRoot, context.pathPrefix, context.repositoryRoot) : context.fernRoot,
|
|
5604
|
+
rawPath,
|
|
5605
|
+
context.repositoryRoot
|
|
5606
|
+
), context.repositoryRoot);
|
|
5607
|
+
} catch {
|
|
5608
|
+
warnOnce2(context, `fern-unsafe-folder-${rawPath}`, `Skipped the Fern folder "${rawPath}" because it points outside the repository (symlink or ..). None of its pages were imported.`);
|
|
5609
|
+
return null;
|
|
5610
|
+
}
|
|
5611
|
+
if (!existsSync3(folder) || !lstatSync3(folder).isDirectory()) {
|
|
5612
|
+
warnOnce2(context, `fern-missing-folder-${rawPath}`, `Fern folder "${rawPath}" does not exist and was skipped.`);
|
|
5613
|
+
return null;
|
|
5614
|
+
}
|
|
5615
|
+
const label = typeof object.title === "string" && object.title.trim() ? object.title.trim() : titleCase2(basename2(folder));
|
|
5616
|
+
const segment = typeof object.slug === "string" || object["skip-slug"] === true ? segmentFor(object, label, context) : fernBasicSlug(basename2(folder));
|
|
5617
|
+
const segments = segment ? [...parentSegments, segment] : parentSegments;
|
|
5618
|
+
const visit = (directory, routeSegments, depth) => {
|
|
5619
|
+
if (depth > 12 || context.descriptors.length >= 5e3) {
|
|
5620
|
+
context.navigationWalk.incomplete = true;
|
|
5621
|
+
warnOnce2(context, "fern-folder-limit", "Fern folder navigation exceeded its depth or page budget; remaining access rules could not be classified.");
|
|
5622
|
+
return [];
|
|
5361
5623
|
}
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
if (
|
|
5367
|
-
|
|
5368
|
-
|
|
5624
|
+
const pages2 = [];
|
|
5625
|
+
const entries = readdirSync(directory, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".") && !entry.isSymbolicLink()).sort((left, right) => Number(left.name !== "index.mdx" && left.name !== "index.md") - Number(right.name !== "index.mdx" && right.name !== "index.md") || left.name.localeCompare(right.name));
|
|
5626
|
+
for (const entry of entries) {
|
|
5627
|
+
const path = resolveWithin(directory, entry.name);
|
|
5628
|
+
if (entry.isDirectory()) {
|
|
5629
|
+
const nested = visit(path, [...routeSegments, fernBasicSlug(entry.name)], depth + 1);
|
|
5630
|
+
if (nested.length) pages2.push({ group: titleCase2(entry.name), pages: nested });
|
|
5631
|
+
} else if (entry.isFile() && [".md", ".mdx"].includes(extname4(entry.name).toLowerCase())) {
|
|
5632
|
+
const stem = basename2(entry.name, extname4(entry.name));
|
|
5633
|
+
const pageSegment = stem.toLowerCase() === "index" ? null : fernBasicSlug(stem);
|
|
5634
|
+
const route = [...routeSegments, ...pageSegment ? [pageSegment] : []].join("/");
|
|
5635
|
+
const source = lstatSync3(path).size <= MAX_CONFIG_BYTES ? readFileSync3(path, "utf8") : "";
|
|
5636
|
+
const frontmatter = source.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1] ?? "";
|
|
5637
|
+
const hidden = /^hidden:\s*true\s*$/m.test(frontmatter);
|
|
5638
|
+
const registered = registerPageAt(relative4(context.fernRoot, path), route, { ...context, pathPrefix: "" }, void 0, hidden);
|
|
5639
|
+
if (registered && !hidden && !context.hiddenInherited) pages2.push(registered);
|
|
5640
|
+
}
|
|
5369
5641
|
}
|
|
5370
|
-
return
|
|
5642
|
+
return pages2;
|
|
5371
5643
|
};
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5644
|
+
const pages = visit(folder, segments, 0);
|
|
5645
|
+
if (!pages.length) return null;
|
|
5646
|
+
addBareRouteRedirect(segments, pages, context);
|
|
5647
|
+
return { group: label, pages };
|
|
5648
|
+
}
|
|
5649
|
+
function registerPage(object, parentSegments, context) {
|
|
5650
|
+
if (typeof object.path !== "string" || !object.path.trim()) return null;
|
|
5651
|
+
const label = typeof object.page === "string" && object.page.trim() ? object.page.trim() : "Untitled";
|
|
5652
|
+
const segment = segmentFor(object, label, context);
|
|
5653
|
+
const base = [...parentSegments, segment].filter(Boolean).join("/");
|
|
5654
|
+
return registerPageAt(object.path, base, context, label, object.hidden === true) || null;
|
|
5655
|
+
}
|
|
5656
|
+
function isRestrictedNode(object) {
|
|
5657
|
+
const viewers = object.viewers;
|
|
5658
|
+
if (Array.isArray(viewers) ? viewers.length > 0 : typeof viewers === "string" ? viewers.trim() !== "" : viewers !== void 0 && viewers !== null && viewers !== false) return true;
|
|
5659
|
+
const authed = object.authed;
|
|
5660
|
+
return authed !== void 0 && authed !== null && authed !== false && !(typeof authed === "string" && ["", "false"].includes(authed.trim().toLowerCase()));
|
|
5661
|
+
}
|
|
5662
|
+
function restrictionMark(context) {
|
|
5663
|
+
return { descriptors: context.descriptors.length, apis: context.apiSections.length };
|
|
5664
|
+
}
|
|
5665
|
+
function markRestricted(context, mark) {
|
|
5666
|
+
for (const descriptor of context.descriptors.slice(mark.descriptors)) descriptor.restricted = true;
|
|
5667
|
+
context.restrictedApiSections.push(...context.apiSections.splice(mark.apis));
|
|
5668
|
+
}
|
|
5669
|
+
function convertNode(node, parentSegments, context) {
|
|
5670
|
+
const object = objectValue3(node);
|
|
5671
|
+
if (!object) return null;
|
|
5672
|
+
const walk2 = context.navigationWalk;
|
|
5673
|
+
const reason = walk2.active.has(object) ? "cyclic section" : walk2.active.size >= 64 ? "section depth exceeds 64" : walk2.visits >= 5e3 ? "node budget exceeds 5000" : void 0;
|
|
5674
|
+
if (reason) {
|
|
5675
|
+
walk2.incomplete = true;
|
|
5676
|
+
warnOnce2(context, `navigation-${reason}`, `Fern navigation was skipped: ${reason}.`);
|
|
5677
|
+
return null;
|
|
5678
|
+
}
|
|
5679
|
+
walk2.visits++;
|
|
5680
|
+
walk2.active.add(object);
|
|
5681
|
+
try {
|
|
5682
|
+
if (isRestrictedNode(object)) {
|
|
5683
|
+
const before = restrictionMark(context);
|
|
5684
|
+
convertNodeContents({ ...object, viewers: void 0, authed: void 0 }, parentSegments, context);
|
|
5685
|
+
markRestricted(context, before);
|
|
5686
|
+
return null;
|
|
5381
5687
|
}
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
const target = pageByRoute.get(item);
|
|
5386
|
-
return [{ title: target?.title ?? item.split("/").at(-1) ?? item, route: item }];
|
|
5387
|
-
}
|
|
5388
|
-
const slug2 = slugifySegment(item.group);
|
|
5389
|
-
const routes = [...item.pages].flatMap((child) => typeof child === "string" ? [child] : []);
|
|
5390
|
-
const route = routes.find((candidate) => candidate === slug2 || candidate.endsWith(`/${slug2}`)) ?? firstRoute(item.pages);
|
|
5391
|
-
return route ? [{ title: item.group, route }] : [];
|
|
5392
|
-
});
|
|
5393
|
-
const markup = cards.length ? `<CardGroup>
|
|
5394
|
-
${cards.map(({ title, route }) => ` <Card title={${JSON.stringify(title)}} href={${JSON.stringify(`/${route === "introduction" ? "" : route}`)}} />`).join("\n")}
|
|
5395
|
-
</CardGroup>` : "";
|
|
5396
|
-
page.body = page.body.replace(/<div data-thally-doc-card-list=""\s*\/>/g, markup);
|
|
5397
|
-
if (markup && !page.body.includes(markup)) page.body = [page.body.trim(), markup].filter(Boolean).join("\n\n");
|
|
5688
|
+
return convertNodeContents(object, parentSegments, context);
|
|
5689
|
+
} finally {
|
|
5690
|
+
walk2.active.delete(object);
|
|
5398
5691
|
}
|
|
5399
5692
|
}
|
|
5400
|
-
function
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
routes.set(sourceKey, descriptor.navigationId);
|
|
5405
|
-
routes.set(sourceReferenceKey(descriptor.docId), descriptor.navigationId);
|
|
5406
|
-
routes.set(sourceReferenceKey(descriptor.navigationId), descriptor.navigationId);
|
|
5407
|
-
const sourceRoute = pageIdFromReference(sourceKey);
|
|
5408
|
-
if (sourceRoute) routes.set(sourceRoute, descriptor.navigationId);
|
|
5693
|
+
function convertNodeContents(object, parentSegments, context) {
|
|
5694
|
+
if (typeof object.page === "string") {
|
|
5695
|
+
const page = registerPage(object, parentSegments, context);
|
|
5696
|
+
return object.hidden === true || context.hiddenInherited ? null : page;
|
|
5409
5697
|
}
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
const suffixIndex = target.search(/[?#]/);
|
|
5414
|
-
const path = suffixIndex >= 0 ? target.slice(0, suffixIndex) : target;
|
|
5415
|
-
const suffix = suffixIndex >= 0 ? target.slice(suffixIndex) : "";
|
|
5416
|
-
if (path.startsWith("/docs/") && options.assetPaths?.has(path.slice("/docs/".length))) {
|
|
5417
|
-
return `/${path.slice("/docs/".length)}${suffix}`;
|
|
5418
|
-
}
|
|
5419
|
-
return options.assetPaths?.has(path.slice(1)) ? target : null;
|
|
5698
|
+
if (typeof object.folder === "string") {
|
|
5699
|
+
const folder = registerFolder(object, parentSegments, object.hidden === true ? { ...context, hiddenInherited: true } : context);
|
|
5700
|
+
return object.hidden === true || context.hiddenInherited ? null : folder;
|
|
5420
5701
|
}
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
const
|
|
5425
|
-
const
|
|
5426
|
-
const
|
|
5427
|
-
const
|
|
5428
|
-
if (
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
sourceReferenceKey(path).replace(/^docs\//, ""),
|
|
5432
|
-
sourceReferenceKey(path).replace(/^docs\/(?:next|current|latest)\//, "")
|
|
5433
|
-
] : [
|
|
5434
|
-
sourceReferenceKey(posix3.normalize(posix3.join(currentDirectory, path))),
|
|
5435
|
-
sourceReferenceKey(path)
|
|
5436
|
-
];
|
|
5437
|
-
const route = candidates.map((candidate) => routes.get(candidate)).find(Boolean);
|
|
5438
|
-
if (!route) {
|
|
5439
|
-
if (path.startsWith("/") && path !== "/" && options.sourceOrigin) {
|
|
5440
|
-
options.onExternalLink?.(target);
|
|
5441
|
-
return new URL(target, options.sourceOrigin).toString();
|
|
5442
|
-
}
|
|
5443
|
-
return target;
|
|
5702
|
+
if (typeof object.section === "string") {
|
|
5703
|
+
const label = object.section;
|
|
5704
|
+
const segment = segmentFor(object, label, context);
|
|
5705
|
+
const segments = segment ? [...parentSegments, segment] : parentSegments;
|
|
5706
|
+
const contents = Array.isArray(object.contents) ? object.contents : [];
|
|
5707
|
+
const pages = [];
|
|
5708
|
+
const hasOwnPath = typeof object.path === "string" && object.path.trim().length > 0;
|
|
5709
|
+
if (hasOwnPath) {
|
|
5710
|
+
const registered = registerPageAt(object.path, segments.join("/"), context, label, object.hidden === true);
|
|
5711
|
+
if (registered) pages.push(registered);
|
|
5444
5712
|
}
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
return body.split("\n").map((line) => {
|
|
5449
|
-
const fence = line.match(/^\s{0,3}(`{3,}|~{3,})/);
|
|
5450
|
-
if (fence) {
|
|
5451
|
-
if (!codeFence) codeFence = fence[1];
|
|
5452
|
-
else if (fence[1][0] === codeFence[0] && fence[1].length >= codeFence.length && /^\s*$/.test(line.slice(fence[0].length))) codeFence = null;
|
|
5453
|
-
return line;
|
|
5454
|
-
}
|
|
5455
|
-
if (codeFence) return line;
|
|
5456
|
-
return line.replace(/(\]\()([^\s)]+)(?=[\s)]|$)/g, (_match, prefix, target) => `${prefix}${rewriteTarget(target)}`).replace(/(\bhref=")([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${rewriteTarget(target)}${suffix}`).replace(/(\bsrc=")([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${copiedAssetTarget(target) ?? target}${suffix}`).replace(/^(\s{0,3}\[[^\]]+\]:\s*)(<[^>]+>|\S+)/, (_match, prefix, destination) => {
|
|
5457
|
-
const bracketed = destination.startsWith("<") && destination.endsWith(">");
|
|
5458
|
-
const target = bracketed ? destination.slice(1, -1) : destination;
|
|
5459
|
-
const rewritten = rewriteTarget(target);
|
|
5460
|
-
return `${prefix}${bracketed ? `<${rewritten}>` : rewritten}`;
|
|
5461
|
-
});
|
|
5462
|
-
}).join("\n");
|
|
5463
|
-
}
|
|
5464
|
-
function readDocusaurusSiteOrigin(repositoryRoot) {
|
|
5465
|
-
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
5466
|
-
if (!configPath) return void 0;
|
|
5467
|
-
const address = readBoundedText(configPath).match(/\burl['"]?\s*:\s*(['"])(https?:\/\/[^'"]+)\1/)?.[2];
|
|
5468
|
-
if (!address) return void 0;
|
|
5469
|
-
try {
|
|
5470
|
-
return new URL(address).origin;
|
|
5471
|
-
} catch {
|
|
5472
|
-
return void 0;
|
|
5473
|
-
}
|
|
5474
|
-
}
|
|
5475
|
-
function readBoundedText(path) {
|
|
5476
|
-
if (lstatSync3(path).size > MAX_CONFIG_BYTES) {
|
|
5477
|
-
throw new Error("The Docusaurus sidebar config is larger than 20 MB and could not be imported.");
|
|
5478
|
-
}
|
|
5479
|
-
return readFileSync3(path, "utf8");
|
|
5480
|
-
}
|
|
5481
|
-
function matchingObjectLiteral(source, start) {
|
|
5482
|
-
let depth = 0;
|
|
5483
|
-
let quote = null;
|
|
5484
|
-
let isEscaped = false;
|
|
5485
|
-
let lineComment = false;
|
|
5486
|
-
let blockComment = false;
|
|
5487
|
-
let objectStart = -1;
|
|
5488
|
-
for (let index = start; index < source.length; index++) {
|
|
5489
|
-
const char = source[index];
|
|
5490
|
-
const next = source[index + 1];
|
|
5491
|
-
if (lineComment) {
|
|
5492
|
-
if (char === "\n") lineComment = false;
|
|
5493
|
-
continue;
|
|
5494
|
-
}
|
|
5495
|
-
if (blockComment) {
|
|
5496
|
-
if (char === "*" && next === "/") {
|
|
5497
|
-
blockComment = false;
|
|
5498
|
-
index++;
|
|
5499
|
-
}
|
|
5500
|
-
continue;
|
|
5501
|
-
}
|
|
5502
|
-
if (quote) {
|
|
5503
|
-
if (isEscaped) isEscaped = false;
|
|
5504
|
-
else if (char === "\\") isEscaped = true;
|
|
5505
|
-
else if (char === quote) quote = null;
|
|
5506
|
-
continue;
|
|
5507
|
-
}
|
|
5508
|
-
if (char === "/" && next === "/") {
|
|
5509
|
-
lineComment = true;
|
|
5510
|
-
index++;
|
|
5511
|
-
continue;
|
|
5512
|
-
}
|
|
5513
|
-
if (char === "/" && next === "*") {
|
|
5514
|
-
blockComment = true;
|
|
5515
|
-
index++;
|
|
5516
|
-
continue;
|
|
5517
|
-
}
|
|
5518
|
-
if (char === '"' || char === "'" || char === "`") {
|
|
5519
|
-
quote = char;
|
|
5520
|
-
continue;
|
|
5521
|
-
}
|
|
5522
|
-
if (char === "{") {
|
|
5523
|
-
if (objectStart < 0) objectStart = index;
|
|
5524
|
-
depth++;
|
|
5525
|
-
} else if (char === "}" && objectStart >= 0) {
|
|
5526
|
-
depth--;
|
|
5527
|
-
if (depth === 0) return source.slice(objectStart, index + 1);
|
|
5528
|
-
} else if (objectStart < 0 && !/\s/.test(char)) {
|
|
5529
|
-
return null;
|
|
5713
|
+
for (const child of contents) {
|
|
5714
|
+
const converted = convertNode(child, segments, object.hidden === true ? { ...context, hiddenInherited: true } : context);
|
|
5715
|
+
if (converted) pages.push(converted);
|
|
5530
5716
|
}
|
|
5717
|
+
if (pages.length === 0) return null;
|
|
5718
|
+
if (!hasOwnPath && segment) addBareRouteRedirect(segments, pages, context);
|
|
5719
|
+
if (object.hidden === true || context.hiddenInherited) return null;
|
|
5720
|
+
return {
|
|
5721
|
+
group: label,
|
|
5722
|
+
...["open-by-default", "closed-by-default", true, false].includes(object.collapsed) ? { defaultOpen: object.collapsed === "open-by-default" || object.collapsed === false } : {},
|
|
5723
|
+
...typeof object.icon === "string" ? { icon: object.icon } : {},
|
|
5724
|
+
...object.hidden === true ? { hidden: true } : {},
|
|
5725
|
+
pages
|
|
5726
|
+
};
|
|
5531
5727
|
}
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
/\b(?:const|let|var)\s+[A-Za-z_$][\w$]*(?:\s*:\s*[^=;]+)?\s*=\s*/g
|
|
5542
|
-
];
|
|
5543
|
-
const candidates = assignmentPatterns.flatMap((pattern) => [...normalizedSource.matchAll(pattern)]).sort((left, right) => (left.index ?? 0) - (right.index ?? 0));
|
|
5544
|
-
for (const candidate of candidates) {
|
|
5545
|
-
const literal = matchingObjectLiteral(normalizedSource, (candidate.index ?? 0) + candidate[0].length);
|
|
5546
|
-
if (!literal) continue;
|
|
5547
|
-
try {
|
|
5548
|
-
const substituted = [...bindings].reduce(
|
|
5549
|
-
(value, [name, binding]) => value.replace(
|
|
5550
|
-
new RegExp(`(:\\s*)${name}\\b`, "g"),
|
|
5551
|
-
(_match, prefix) => `${prefix}${JSON.stringify(binding)}`
|
|
5552
|
-
),
|
|
5553
|
-
literal
|
|
5554
|
-
);
|
|
5555
|
-
const parsed = JSON5.parse(substituted);
|
|
5556
|
-
const object = objectValue3(parsed);
|
|
5557
|
-
if (!object) continue;
|
|
5558
|
-
const bindingName = candidate[0].match(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/)?.[1];
|
|
5559
|
-
if (bindingName) {
|
|
5560
|
-
bindings.set(bindingName, object);
|
|
5561
|
-
continue;
|
|
5728
|
+
if (typeof object.link === "string") {
|
|
5729
|
+
const label = object.link.trim();
|
|
5730
|
+
const rawHref = typeof object.href === "string" ? object.href.trim() : "";
|
|
5731
|
+
let href;
|
|
5732
|
+
if (/^https?:\/\//i.test(rawHref) && !/[\\\u0000-\u0020\u007f]/.test(rawHref)) {
|
|
5733
|
+
try {
|
|
5734
|
+
const url = new URL(rawHref);
|
|
5735
|
+
if (["http:", "https:"].includes(url.protocol) && !url.username && !url.password) href = url.toString();
|
|
5736
|
+
} catch {
|
|
5562
5737
|
}
|
|
5563
|
-
return object;
|
|
5564
|
-
} catch (error) {
|
|
5565
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
5566
|
-
const line = Number(message.match(/at (\d+):/)?.[1] ?? 0);
|
|
5567
|
-
const context = line > 0 ? literal.split("\n").slice(Math.max(0, line - 2), line + 1).join(" ") : "";
|
|
5568
|
-
parseFailures.push(`${message}${context ? ` near ${context}` : ""}`);
|
|
5569
|
-
}
|
|
5570
|
-
}
|
|
5571
|
-
const exportedBinding = normalizedSource.match(/\bexport\s+default\s+([A-Za-z_$][\w$]*)\b/)?.[1] ?? normalizedSource.match(/\bmodule\.exports\s*=\s*([A-Za-z_$][\w$]*)\b/)?.[1];
|
|
5572
|
-
if (exportedBinding && bindings.has(exportedBinding)) return bindings.get(exportedBinding);
|
|
5573
|
-
throw new Error(`Sidebar config is executable or outside the supported data-only syntax.${parseFailures[0] ? ` ${parseFailures[0]}` : ""}`);
|
|
5574
|
-
}
|
|
5575
|
-
function replaceExternalFbContent(source) {
|
|
5576
|
-
const marker = "...fbContent(";
|
|
5577
|
-
let result = source;
|
|
5578
|
-
let searchFrom = 0;
|
|
5579
|
-
while (true) {
|
|
5580
|
-
const start = result.indexOf(marker, searchFrom);
|
|
5581
|
-
if (start < 0) return result.replace(/,\s*,/g, ",");
|
|
5582
|
-
const objectStart = result.indexOf("{", start + marker.length);
|
|
5583
|
-
if (objectStart < 0) return result;
|
|
5584
|
-
const objectLiteral = matchingObjectLiteral(result, objectStart);
|
|
5585
|
-
if (!objectLiteral) {
|
|
5586
|
-
searchFrom = start + marker.length;
|
|
5587
|
-
continue;
|
|
5588
5738
|
}
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
const externalMatch = /\bexternal\s*:\s*/g.exec(objectLiteral);
|
|
5593
|
-
let replacement = "";
|
|
5594
|
-
if (externalMatch) {
|
|
5595
|
-
const arrayStart = objectLiteral.indexOf("[", externalMatch.index + externalMatch[0].length);
|
|
5596
|
-
if (arrayStart >= 0) {
|
|
5597
|
-
const array = matchingArrayLiteral(objectLiteral, arrayStart);
|
|
5598
|
-
if (array) replacement = array.slice(1, -1);
|
|
5739
|
+
if (label && href && object.hidden !== true && !context.hiddenInherited) {
|
|
5740
|
+
if (!context.externalLinks.some((link) => link.label === label && link.href === href)) {
|
|
5741
|
+
context.externalLinks.push({ label, href });
|
|
5599
5742
|
}
|
|
5743
|
+
} else if (!href && object.hidden !== true && !context.hiddenInherited) {
|
|
5744
|
+
warnOnce2(context, `fern-link-${rawHref}`, `Fern navigation link "${label}" has an unsafe or invalid external URL and was skipped.`);
|
|
5600
5745
|
}
|
|
5601
|
-
|
|
5602
|
-
if (!replacement) {
|
|
5603
|
-
const trailingComma = result.slice(replaceEnd).match(/^\s*,/);
|
|
5604
|
-
if (trailingComma) replaceEnd += trailingComma[0].length;
|
|
5605
|
-
}
|
|
5606
|
-
result = `${result.slice(0, start)}${replacement}${result.slice(replaceEnd)}`;
|
|
5607
|
-
searchFrom = start + replacement.length;
|
|
5746
|
+
return null;
|
|
5608
5747
|
}
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
if (isEscaped) isEscaped = false;
|
|
5618
|
-
else if (character === "\\") isEscaped = true;
|
|
5619
|
-
else if (character === quote) quote = "";
|
|
5620
|
-
continue;
|
|
5621
|
-
}
|
|
5622
|
-
if (character === '"' || character === "'" || character === "`") {
|
|
5623
|
-
quote = character;
|
|
5624
|
-
continue;
|
|
5625
|
-
}
|
|
5626
|
-
if (character === "[") depth++;
|
|
5627
|
-
if (character === "]" && --depth === 0) return source.slice(start, index + 1);
|
|
5748
|
+
if (typeof object.api === "string") {
|
|
5749
|
+
const ownSegment = segmentFor(object, object.api, context);
|
|
5750
|
+
context.apiSections.push({
|
|
5751
|
+
name: typeof object["api-name"] === "string" ? object["api-name"] : object.api,
|
|
5752
|
+
nameExplicit: typeof object["api-name"] === "string",
|
|
5753
|
+
routeSegments: ownSegment ? [...parentSegments, ownSegment] : parentSegments
|
|
5754
|
+
});
|
|
5755
|
+
return null;
|
|
5628
5756
|
}
|
|
5629
|
-
|
|
5630
|
-
}
|
|
5631
|
-
function configuredSidebarPath(repositoryRoot) {
|
|
5632
|
-
for (const filename of ["docusaurus.config.js", "docusaurus.config.ts", "docusaurus.config.mjs"]) {
|
|
5633
|
-
const path = resolveWithin(repositoryRoot, filename);
|
|
5634
|
-
if (!existsSync3(path)) continue;
|
|
5635
|
-
const source = readBoundedText(path);
|
|
5636
|
-
for (const docs of source.matchAll(/\bdocs\s*:\s*/g)) {
|
|
5637
|
-
const object = matchingObjectLiteral(source, (docs.index ?? 0) + docs[0].length);
|
|
5638
|
-
const candidate2 = object && staticStringField(object, "sidebarPath")?.replace(/^\.\//, "");
|
|
5639
|
-
if (candidate2) {
|
|
5640
|
-
const resolved2 = resolveWithin(repositoryRoot, candidate2);
|
|
5641
|
-
if (existsSync3(resolved2) && lstatSync3(resolved2).isFile()) return candidate2;
|
|
5642
|
-
}
|
|
5643
|
-
}
|
|
5644
|
-
const match = source.match(/\bsidebarPath\s*:\s*(?:require\.resolve\(\s*)?(['"])([^'"]+)\1/);
|
|
5645
|
-
if (!match) continue;
|
|
5646
|
-
const candidate = match[2].replace(/^\.\//, "");
|
|
5647
|
-
const resolved = resolveWithin(repositoryRoot, candidate);
|
|
5648
|
-
if (existsSync3(resolved) && lstatSync3(resolved).isFile()) return candidate;
|
|
5757
|
+
if (typeof object.changelog === "string") {
|
|
5758
|
+
return registerFolder({ folder: object.changelog, title: "Changelog", "skip-slug": true }, parentSegments, context);
|
|
5649
5759
|
}
|
|
5650
5760
|
return null;
|
|
5651
5761
|
}
|
|
5652
|
-
function
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
return existsSync3(path) && lstatSync3(path).isFile();
|
|
5762
|
+
function convertNodes(nodes, parentSegments, context) {
|
|
5763
|
+
return nodes.flatMap((node) => {
|
|
5764
|
+
const converted = convertNode(node, parentSegments, context);
|
|
5765
|
+
return converted ? [converted] : [];
|
|
5657
5766
|
});
|
|
5658
|
-
if (!sourcePath) return null;
|
|
5659
|
-
const absolutePath = resolveWithin(repositoryRoot, sourcePath);
|
|
5660
|
-
const source = readBoundedText(absolutePath);
|
|
5661
|
-
const parsed = extname4(sourcePath).toLowerCase() === ".json" ? objectValue3(JSON5.parse(source)) : parseStaticSidebarModule(source);
|
|
5662
|
-
if (!parsed) throw new Error("Docusaurus sidebar config must export an object.");
|
|
5663
|
-
return { config: parsed, sourcePath };
|
|
5664
5767
|
}
|
|
5665
|
-
function
|
|
5666
|
-
const
|
|
5667
|
-
const
|
|
5668
|
-
|
|
5669
|
-
return matchingObjectLiteral(normalizedSource, match.index + match[0].length);
|
|
5768
|
+
function groupsFromConverted(converted) {
|
|
5769
|
+
const loosePages = converted.filter((entry) => typeof entry === "string");
|
|
5770
|
+
const groups = converted.filter((entry) => typeof entry !== "string");
|
|
5771
|
+
return loosePages.length > 0 ? [{ group: "Overview", pages: loosePages }, ...groups] : groups;
|
|
5670
5772
|
}
|
|
5671
|
-
function
|
|
5672
|
-
|
|
5673
|
-
if (
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
const arrayLiteral = matchingArrayLiteral(objectLiteral, arrayStart);
|
|
5677
|
-
if (!arrayLiteral) return [];
|
|
5678
|
-
let parsed;
|
|
5679
|
-
try {
|
|
5680
|
-
parsed = JSON5.parse(arrayLiteral);
|
|
5681
|
-
} catch {
|
|
5682
|
-
return [];
|
|
5773
|
+
function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
5774
|
+
if (Array.isArray(config.navigation)) return config;
|
|
5775
|
+
if (Array.isArray(config.products)) {
|
|
5776
|
+
warnOnce2(context, "fern-products", "Nested Fern multi-product docs are not supported; only this docs.yml was imported.");
|
|
5777
|
+
return config;
|
|
5683
5778
|
}
|
|
5684
|
-
if (!Array.isArray(
|
|
5685
|
-
const
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5779
|
+
if (!Array.isArray(config.versions)) return config;
|
|
5780
|
+
const versions = config.versions.flatMap((entry) => {
|
|
5781
|
+
const version = objectValue3(entry);
|
|
5782
|
+
return version ? [version] : [];
|
|
5783
|
+
});
|
|
5784
|
+
const chosen = versions.find((version) => version.default === true) ?? versions[0];
|
|
5785
|
+
if (chosen && isRestrictedNode(chosen)) context.versionRestricted = true;
|
|
5786
|
+
const skipped = versions.filter((version) => version !== chosen).map((version) => typeof version.version === "string" ? version.version : void 0).filter((name) => Boolean(name));
|
|
5787
|
+
if (skipped.length > 0) {
|
|
5788
|
+
warnOnce2(
|
|
5789
|
+
context,
|
|
5790
|
+
"fern-versions",
|
|
5791
|
+
`Only the default Fern version was imported; other versions were skipped: ${skipped.join(", ")}.`
|
|
5792
|
+
);
|
|
5695
5793
|
}
|
|
5696
|
-
return
|
|
5697
|
-
}
|
|
5698
|
-
function readDocusaurusRedirects(repositoryRoot, warnings) {
|
|
5699
|
-
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
5700
|
-
if (!configPath) return [];
|
|
5701
|
-
const configSource = readBoundedText(configPath);
|
|
5702
|
-
const pluginMatch = /['"]@docusaurus\/plugin-client-redirects['"]\s*,\s*/.exec(configSource);
|
|
5703
|
-
if (!pluginMatch) return [];
|
|
5704
|
-
const afterPlugin = configSource.slice(pluginMatch.index + pluginMatch[0].length);
|
|
5705
|
-
const warnAboutCreateRedirects = (source) => {
|
|
5706
|
-
if (!/\bcreateRedirects\s*[:(]/.test(source)) return;
|
|
5707
|
-
warnings.push({
|
|
5708
|
-
code: "unsupported-config",
|
|
5709
|
-
message: "The redirects plugin's createRedirects function computes redirects dynamically and cannot be evaluated during migration; add its redirects manually."
|
|
5710
|
-
});
|
|
5711
|
-
};
|
|
5712
|
-
warnAboutCreateRedirects(configSource);
|
|
5713
|
-
const inlineLiteral = matchingObjectLiteral(configSource, pluginMatch.index + pluginMatch[0].length);
|
|
5714
|
-
if (inlineLiteral) return redirectEntriesFromObjectLiteralText(inlineLiteral);
|
|
5715
|
-
const identifier = afterPlugin.match(/^([A-Za-z_$][\w$]*)/)?.[1];
|
|
5716
|
-
if (!identifier) return [];
|
|
5717
|
-
const importMatch = new RegExp(`import\\s*\\{[^}]*\\b${identifier}\\b[^}]*\\}\\s*from\\s*(['"])([^'"]+)\\1`).exec(configSource) ?? new RegExp(`import\\s+${identifier}\\s+from\\s*(['"])([^'"]+)\\1`).exec(configSource);
|
|
5718
|
-
const modulePath = importMatch?.[2];
|
|
5719
|
-
if (!modulePath || !modulePath.startsWith(".")) return [];
|
|
5720
|
-
let resolvedPath;
|
|
5794
|
+
if (!chosen || typeof chosen.path !== "string") return config;
|
|
5721
5795
|
try {
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
if (!resolvedPath) return [];
|
|
5727
|
-
const moduleSource = readBoundedText(resolvedPath);
|
|
5728
|
-
warnAboutCreateRedirects(moduleSource);
|
|
5729
|
-
const literal = namedExportObjectLiteralText(moduleSource, identifier);
|
|
5730
|
-
return literal ? redirectEntriesFromObjectLiteralText(literal) : [];
|
|
5731
|
-
}
|
|
5732
|
-
function readCategoryMetadata(contentRoot, directory) {
|
|
5733
|
-
for (const filename of CATEGORY_FILENAMES) {
|
|
5734
|
-
const path = resolveWithin(contentRoot, posix3.join(directory, filename));
|
|
5735
|
-
if (!existsSync3(path) || !lstatSync3(path).isFile()) continue;
|
|
5736
|
-
try {
|
|
5737
|
-
const raw = readBoundedText(path);
|
|
5738
|
-
const parsed = filename.endsWith(".json") ? JSON5.parse(raw) : parseYaml2(raw);
|
|
5739
|
-
const object = objectValue3(parsed);
|
|
5740
|
-
if (!object) return {};
|
|
5741
|
-
return {
|
|
5742
|
-
...typeof object.label === "string" ? { label: object.label } : {},
|
|
5743
|
-
...typeof object.position === "number" ? { position: object.position } : {},
|
|
5744
|
-
..."link" in object ? { link: object.link } : {}
|
|
5745
|
-
};
|
|
5746
|
-
} catch {
|
|
5747
|
-
return {};
|
|
5796
|
+
const versionPath = resolveWithinRoot(configDir, chosen.path, context.repositoryRoot);
|
|
5797
|
+
if (!existsSync3(versionPath) || !lstatSync3(versionPath).isFile()) {
|
|
5798
|
+
warnOnce2(context, "fern-version-not-file", `Fern version file "${chosen.path}" is not a regular file and was skipped.`);
|
|
5799
|
+
return config;
|
|
5748
5800
|
}
|
|
5801
|
+
withinRealRoot(versionPath, context.repositoryRoot);
|
|
5802
|
+
const versionConfig = objectValue3(readBoundedYaml(versionPath));
|
|
5803
|
+
if (!versionConfig) return config;
|
|
5804
|
+
context.pathPrefix = relative4(fernRoot, dirname3(versionPath)).replace(/\\/g, "/");
|
|
5805
|
+
return { ...config, navigation: versionConfig.navigation, tabs: versionConfig.tabs ?? config.tabs };
|
|
5806
|
+
} catch (error) {
|
|
5807
|
+
warnOnce2(
|
|
5808
|
+
context,
|
|
5809
|
+
"fern-version-read-failed",
|
|
5810
|
+
isPathEscapeError(error) ? `Skipped the Fern version file "${chosen.path}" because it points outside the repository (symlink or ..).` : `Fern version file "${chosen.path}" could not be read (${error instanceof Error ? error.message : String(error)}) and was skipped.`
|
|
5811
|
+
);
|
|
5812
|
+
return config;
|
|
5749
5813
|
}
|
|
5750
|
-
return {};
|
|
5751
|
-
}
|
|
5752
|
-
function descriptorSort(left, right) {
|
|
5753
|
-
const leftPosition = left.sidebarPosition ?? Number.MAX_SAFE_INTEGER;
|
|
5754
|
-
const rightPosition = right.sidebarPosition ?? Number.MAX_SAFE_INTEGER;
|
|
5755
|
-
if (leftPosition !== rightPosition) return leftPosition - rightPosition;
|
|
5756
|
-
return left.sourcePath.localeCompare(right.sourcePath, void 0, { numeric: true });
|
|
5757
5814
|
}
|
|
5758
|
-
function
|
|
5759
|
-
const
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
context
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
});
|
|
5768
|
-
return null;
|
|
5815
|
+
function buildTabsFromConfig(rawConfig, configDir, fernRoot, routePrefix, fallbackTabLabel, context) {
|
|
5816
|
+
const before = restrictionMark(context);
|
|
5817
|
+
let tabs;
|
|
5818
|
+
let restricted;
|
|
5819
|
+
try {
|
|
5820
|
+
tabs = buildUnrestrictedTabs(rawConfig, configDir, fernRoot, routePrefix, fallbackTabLabel, context);
|
|
5821
|
+
restricted = context.versionRestricted === true;
|
|
5822
|
+
} finally {
|
|
5823
|
+
context.versionRestricted = false;
|
|
5769
5824
|
}
|
|
5770
|
-
|
|
5771
|
-
|
|
5825
|
+
if (!restricted) return tabs;
|
|
5826
|
+
markRestricted(context, before);
|
|
5827
|
+
return [];
|
|
5772
5828
|
}
|
|
5773
|
-
function
|
|
5774
|
-
const
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5779
|
-
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5829
|
+
function buildUnrestrictedTabs(rawConfig, configDir, fernRoot, routePrefix, fallbackTabLabel, context) {
|
|
5830
|
+
const config = resolveVersionedNavigation(rawConfig, configDir, fernRoot, context);
|
|
5831
|
+
const tabsMeta = objectValue3(config.tabs) ?? {};
|
|
5832
|
+
const navigation = config.navigation;
|
|
5833
|
+
const isTabbedNavigation = Array.isArray(navigation) && navigation.length > 0 && navigation.every((entry) => typeof objectValue3(entry)?.tab === "string");
|
|
5834
|
+
if (isTabbedNavigation) {
|
|
5835
|
+
return navigation.flatMap((entry) => {
|
|
5836
|
+
const id = String(entry.tab);
|
|
5837
|
+
const meta = objectValue3(tabsMeta[id]) ?? {};
|
|
5838
|
+
const label = typeof meta["display-name"] === "string" ? meta["display-name"] : titleCase2(id);
|
|
5839
|
+
const tabSegment = segmentFor(meta, label, context);
|
|
5840
|
+
const segments = [...routePrefix, ...tabSegment ? [tabSegment] : []];
|
|
5841
|
+
const layout = Array.isArray(entry.layout) ? entry.layout : [];
|
|
5842
|
+
const sectionsBefore = context.apiSections.length;
|
|
5843
|
+
const descriptorsBefore = restrictionMark(context);
|
|
5844
|
+
const groups = groupsFromConverted(convertNodes(layout, segments, context));
|
|
5845
|
+
if (isRestrictedNode(entry) || isRestrictedNode(meta)) {
|
|
5846
|
+
markRestricted(context, descriptorsBefore);
|
|
5847
|
+
return [];
|
|
5848
|
+
}
|
|
5849
|
+
for (let index = sectionsBefore; index < context.apiSections.length; index += 1) {
|
|
5850
|
+
context.apiSections[index].tabLabel ??= label;
|
|
5851
|
+
}
|
|
5852
|
+
if (groups.length === 0 && typeof meta.href === "string" && /^https?:\/\//i.test(meta.href)) {
|
|
5853
|
+
return [{ tab: label, href: meta.href }];
|
|
5854
|
+
}
|
|
5855
|
+
if (groups.length === 0 && typeof meta.changelog === "string") {
|
|
5856
|
+
const changelog = registerFolder({ folder: meta.changelog, title: label, "skip-slug": true }, segments, context);
|
|
5857
|
+
if (changelog) {
|
|
5858
|
+
const indexRoute = segments.join("/");
|
|
5859
|
+
const descriptors = new Map(context.descriptors.map((descriptor) => [descriptor.navigationId, descriptor]));
|
|
5860
|
+
const visiblePages = changelog.pages.filter((page) => typeof page === "string").filter((page) => !descriptors.get(page)?.hidden);
|
|
5861
|
+
const index = visiblePages.find((page) => {
|
|
5862
|
+
const descriptor = descriptors.get(page);
|
|
5863
|
+
if (!descriptor) return false;
|
|
5864
|
+
const stem = basename2(descriptor.sourcePath, extname4(descriptor.sourcePath));
|
|
5865
|
+
return page === indexRoute || stem === "overview" || stem === "index";
|
|
5866
|
+
});
|
|
5867
|
+
const entries = visiblePages.filter((page) => page !== index).sort((left, right) => {
|
|
5868
|
+
const date = (page) => descriptors.get(page)?.sourcePath.match(/\d{4}-\d{2}-\d{2}/)?.[0] ?? "";
|
|
5869
|
+
return date(right).localeCompare(date(left)) || left.localeCompare(right);
|
|
5870
|
+
});
|
|
5871
|
+
for (const entryId of entries) {
|
|
5872
|
+
const descriptor = descriptors.get(entryId);
|
|
5873
|
+
if (descriptor) descriptor.hidden = true;
|
|
5874
|
+
}
|
|
5875
|
+
changelog.pages = [index ?? indexRoute];
|
|
5876
|
+
context.changelogIndexes.push({ route: indexRoute, entries });
|
|
5877
|
+
const latest = index ?? firstLeafId(changelog.pages);
|
|
5878
|
+
const bareRouteIndex = context.bareRouteRedirects.findIndex((redirect) => redirect.source === `/${indexRoute}`);
|
|
5879
|
+
const indexDescriptor = index ? descriptors.get(index) : void 0;
|
|
5880
|
+
const indexSource = indexDescriptor && lstatSync3(resolveWithinRoot(context.fernRoot, indexDescriptor.sourcePath, context.repositoryRoot)).size <= MAX_CONFIG_BYTES ? readFileSync3(resolveWithinRoot(context.fernRoot, indexDescriptor.sourcePath, context.repositoryRoot), "utf8") : "";
|
|
5881
|
+
const indexFrontmatter = indexSource.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1] ?? "";
|
|
5882
|
+
const indexSlug = indexFrontmatter.match(/^slug:\s*["']?([^\s"']+)["']?\s*$/m)?.[1]?.replace(/^\/+|\/+$/g, "");
|
|
5883
|
+
const hasBarePage = index === indexRoute || indexSlug === indexRoute;
|
|
5884
|
+
if (bareRouteIndex >= 0 && hasBarePage) context.bareRouteRedirects.splice(bareRouteIndex, 1);
|
|
5885
|
+
else if (latest && bareRouteIndex >= 0) context.bareRouteRedirects[bareRouteIndex].destination = `/${latest}`;
|
|
5886
|
+
return [{ tab: label, groups: [changelog] }];
|
|
5887
|
+
}
|
|
5888
|
+
}
|
|
5889
|
+
if (groups.length === 0) return [];
|
|
5890
|
+
if (segments.length > 0) addBareRouteRedirect(segments, groups, context);
|
|
5891
|
+
return [{
|
|
5892
|
+
tab: label,
|
|
5893
|
+
...typeof meta.icon === "string" ? { icon: meta.icon } : {},
|
|
5894
|
+
groups
|
|
5895
|
+
}];
|
|
5791
5896
|
});
|
|
5792
|
-
context.generatedIds.add(navigationId);
|
|
5793
5897
|
}
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
const configSource = readBoundedText(configPath);
|
|
5805
|
-
const cssRelativePath = configSource.match(/\bcustomCss\s*:\s*(?:\[\s*)?(?:require\.resolve\(\s*)?(['"])([^'"]+)\1/)?.[2];
|
|
5806
|
-
if (!cssRelativePath) return void 0;
|
|
5807
|
-
let cssPath;
|
|
5808
|
-
try {
|
|
5809
|
-
cssPath = resolveWithin(repositoryRoot, cssRelativePath.replace(/^\.\//, ""));
|
|
5810
|
-
} catch {
|
|
5811
|
-
return void 0;
|
|
5898
|
+
if (Array.isArray(navigation)) {
|
|
5899
|
+
const sectionsBefore = context.apiSections.length;
|
|
5900
|
+
const groups = groupsFromConverted(convertNodes(navigation, routePrefix, context));
|
|
5901
|
+
for (let index = sectionsBefore; index < context.apiSections.length; index += 1) {
|
|
5902
|
+
context.apiSections[index].tabLabel ??= fallbackTabLabel;
|
|
5903
|
+
}
|
|
5904
|
+
if (groups.length > 0) {
|
|
5905
|
+
if (routePrefix.length > 0) addBareRouteRedirect(routePrefix, groups, context);
|
|
5906
|
+
return [{ tab: fallbackTabLabel, groups }];
|
|
5907
|
+
}
|
|
5812
5908
|
}
|
|
5813
|
-
|
|
5814
|
-
const css = readBoundedText(cssPath);
|
|
5815
|
-
const pick = (block) => {
|
|
5816
|
-
const value = block?.match(/--ifm-color-primary\s*:\s*([^;]+);/)?.[1]?.trim();
|
|
5817
|
-
return value && DOCUSAURUS_HEX_COLOR.test(value) ? value : void 0;
|
|
5818
|
-
};
|
|
5819
|
-
const lightMode = pick(css.match(/:root\s*\{([^}]*)\}/)?.[1]);
|
|
5820
|
-
const darkMode = pick(css.match(/\[data-theme=(['"])dark\1\]\s*\{([^}]*)\}/)?.[2]);
|
|
5821
|
-
const colors = {};
|
|
5822
|
-
if (darkMode) colors.light = darkMode;
|
|
5823
|
-
if (lightMode) colors.dark = lightMode;
|
|
5824
|
-
return Object.keys(colors).length > 0 ? colors : void 0;
|
|
5825
|
-
}
|
|
5826
|
-
function propertyStart(source, name) {
|
|
5827
|
-
const pattern = new RegExp(`(?:^|[,{\\n])\\s*["']?${name}["']?\\s*:\\s*`, "m");
|
|
5828
|
-
const match = pattern.exec(source);
|
|
5829
|
-
return match ? match.index + match[0].length : -1;
|
|
5909
|
+
return [];
|
|
5830
5910
|
}
|
|
5831
|
-
function
|
|
5832
|
-
|
|
5833
|
-
|
|
5911
|
+
function projectFernProducts(rawProducts, fernRoot, context) {
|
|
5912
|
+
return rawProducts.flatMap((entry) => {
|
|
5913
|
+
const product = objectValue3(entry);
|
|
5914
|
+
const rawPath = typeof product?.path === "string" ? product.path.trim() : "";
|
|
5915
|
+
if (!product || !rawPath) return [];
|
|
5916
|
+
const label = typeof product["display-name"] === "string" && product["display-name"].trim() ? product["display-name"].trim() : "Product";
|
|
5917
|
+
let productPath;
|
|
5834
5918
|
try {
|
|
5835
|
-
|
|
5836
|
-
if (typeof value === "string") bindings.set(match[1], value);
|
|
5919
|
+
productPath = withinRealRoot(resolveWithin(fernRoot, rawPath), context.repositoryRoot);
|
|
5837
5920
|
} catch {
|
|
5921
|
+
warnOnce2(context, `fern-product-unsafe-${rawPath}`, `Skipped the Fern product "${label}" because "${rawPath}" points outside the repository (symlink or ..).`);
|
|
5922
|
+
return [];
|
|
5838
5923
|
}
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
function staticArrayField(source, name) {
|
|
5843
|
-
const start = propertyStart(source, name);
|
|
5844
|
-
return start < 0 ? null : matchingArrayLiteral(source, start);
|
|
5845
|
-
}
|
|
5846
|
-
function staticObjectEntries(array) {
|
|
5847
|
-
if (!array) return [];
|
|
5848
|
-
const entries = [];
|
|
5849
|
-
let afterDelimiter = 1;
|
|
5850
|
-
let quote = "";
|
|
5851
|
-
let lineComment = false;
|
|
5852
|
-
let blockComment = false;
|
|
5853
|
-
for (let index = 1; index < array.length - 1; index++) {
|
|
5854
|
-
const char = array[index];
|
|
5855
|
-
const next = array[index + 1];
|
|
5856
|
-
if (lineComment) {
|
|
5857
|
-
if (char === "\n") lineComment = false;
|
|
5858
|
-
continue;
|
|
5924
|
+
if (!existsSync3(productPath) || !lstatSync3(productPath).isFile()) {
|
|
5925
|
+
warnOnce2(context, `fern-product-missing-${rawPath}`, `Fern product "${label}" path "${rawPath}" does not exist and was skipped.`);
|
|
5926
|
+
return [];
|
|
5859
5927
|
}
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5928
|
+
let productConfig;
|
|
5929
|
+
try {
|
|
5930
|
+
productConfig = objectValue3(readBoundedYaml(productPath));
|
|
5931
|
+
} catch (error) {
|
|
5932
|
+
warnOnce2(
|
|
5933
|
+
context,
|
|
5934
|
+
`fern-product-read-failed-${rawPath}`,
|
|
5935
|
+
`Fern product "${label}" could not be read (${error instanceof Error ? error.message : String(error)}) and was skipped.`
|
|
5936
|
+
);
|
|
5937
|
+
return [];
|
|
5866
5938
|
}
|
|
5867
|
-
if (
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5939
|
+
if (!productConfig) return [];
|
|
5940
|
+
const productDir = dirname3(productPath);
|
|
5941
|
+
const routeSegment = segmentFor(product, label, context);
|
|
5942
|
+
const priorPrefix = context.pathPrefix;
|
|
5943
|
+
context.pathPrefix = relative4(fernRoot, productDir).replace(/\\/g, "/");
|
|
5944
|
+
const before = restrictionMark(context);
|
|
5945
|
+
try {
|
|
5946
|
+
const tabs = buildTabsFromConfig(
|
|
5947
|
+
productConfig,
|
|
5948
|
+
productDir,
|
|
5949
|
+
fernRoot,
|
|
5950
|
+
routeSegment ? [routeSegment] : [],
|
|
5951
|
+
label,
|
|
5952
|
+
context
|
|
5953
|
+
);
|
|
5954
|
+
if (!isRestrictedNode(product)) return tabs;
|
|
5955
|
+
markRestricted(context, before);
|
|
5956
|
+
return [];
|
|
5957
|
+
} finally {
|
|
5958
|
+
context.pathPrefix = priorPrefix;
|
|
5874
5959
|
}
|
|
5875
|
-
|
|
5876
|
-
lineComment = true;
|
|
5877
|
-
index++;
|
|
5878
|
-
continue;
|
|
5879
|
-
}
|
|
5880
|
-
if (char === "/" && next === "*") {
|
|
5881
|
-
blockComment = true;
|
|
5882
|
-
index++;
|
|
5883
|
-
continue;
|
|
5884
|
-
}
|
|
5885
|
-
if (char === '"' || char === "'" || char === "`") {
|
|
5886
|
-
quote = char;
|
|
5887
|
-
continue;
|
|
5888
|
-
}
|
|
5889
|
-
if (char === ",") {
|
|
5890
|
-
afterDelimiter = index + 1;
|
|
5891
|
-
continue;
|
|
5892
|
-
}
|
|
5893
|
-
if (char !== "{") continue;
|
|
5894
|
-
const literal = matchingObjectLiteral(array, index);
|
|
5895
|
-
if (!literal) continue;
|
|
5896
|
-
const lead = array.slice(afterDelimiter, index).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, "").trim();
|
|
5897
|
-
if (!lead) entries.push(literal);
|
|
5898
|
-
index += literal.length - 1;
|
|
5899
|
-
}
|
|
5900
|
-
return entries;
|
|
5960
|
+
});
|
|
5901
5961
|
}
|
|
5902
|
-
function
|
|
5903
|
-
const
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5962
|
+
function projectFernNavigation(input) {
|
|
5963
|
+
const context = {
|
|
5964
|
+
navigationWalk: { active: /* @__PURE__ */ new Set(), visits: 0, incomplete: false },
|
|
5965
|
+
fernRoot: input.fernRoot,
|
|
5966
|
+
repositoryRoot: input.repositoryRoot ?? input.fernRoot,
|
|
5967
|
+
descriptors: [],
|
|
5968
|
+
seenNavigationIds: /* @__PURE__ */ new Set(),
|
|
5969
|
+
apiSections: [],
|
|
5970
|
+
restrictedApiSections: [],
|
|
5971
|
+
changelogIndexes: [],
|
|
5972
|
+
warnings: [],
|
|
5973
|
+
warningKeys: /* @__PURE__ */ new Set(),
|
|
5974
|
+
externalLinks: [],
|
|
5975
|
+
bareRouteRedirects: [],
|
|
5976
|
+
segmentAliases: /* @__PURE__ */ new Map(),
|
|
5977
|
+
pathPrefix: "",
|
|
5978
|
+
sourcePathToNavigationId: /* @__PURE__ */ new Map()
|
|
5979
|
+
};
|
|
5980
|
+
const config = input.config;
|
|
5981
|
+
const hasProducts = Array.isArray(config.products) && config.products.length > 0 && !Array.isArray(config.navigation);
|
|
5982
|
+
const tabs = hasProducts ? projectFernProducts(config.products, input.fernRoot, context) : buildTabsFromConfig(config, input.fernRoot, input.fernRoot, [], "Documentation", context);
|
|
5983
|
+
if (tabs.length === 0) {
|
|
5984
|
+
context.warnings.push({
|
|
5985
|
+
code: "unsupported-config",
|
|
5986
|
+
message: "Fern navigation could not be projected; generated navigation will be used."
|
|
5987
|
+
});
|
|
5909
5988
|
}
|
|
5910
|
-
|
|
5911
|
-
const
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
return
|
|
5989
|
+
const navbarLinks = Array.isArray(config["navbar-links"]) ? config["navbar-links"].flatMap((entry) => {
|
|
5990
|
+
const link = objectValue3(entry);
|
|
5991
|
+
const href = typeof link?.url === "string" ? link.url : typeof link?.href === "string" ? link.href : typeof link?.value === "string" ? link.value : void 0;
|
|
5992
|
+
const label = typeof link?.text === "string" ? link.text : typeof link?.label === "string" ? link.label : link?.type === "github" ? "GitHub" : void 0;
|
|
5993
|
+
return href && label ? [{ label, href, ...link?.type === "github" ? { type: "github" } : {} }] : [];
|
|
5994
|
+
}) : [];
|
|
5995
|
+
for (const link of context.externalLinks) {
|
|
5996
|
+
if (!navbarLinks.some((existing) => existing.label === link.label && existing.href === link.href)) {
|
|
5997
|
+
navbarLinks.push(link);
|
|
5998
|
+
}
|
|
5915
5999
|
}
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
const
|
|
5919
|
-
const
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
if (propertyStart(item, "items") >= 0 || propertyStart(item, "dropdownItemsAfter") >= 0) return [];
|
|
5940
|
-
const docsPluginId = staticStringField(item, "docsPluginId", bindings);
|
|
5941
|
-
const href = staticStringField(item, "to", bindings) ?? staticStringField(item, "href", bindings);
|
|
5942
|
-
const docId = staticStringField(item, "docId", bindings);
|
|
5943
|
-
const sidebarId = staticStringField(item, "sidebarId", bindings);
|
|
5944
|
-
const label = staticStringField(item, "label", bindings) ?? staticStringField(item, "aria-label", bindings);
|
|
5945
|
-
return label && (href || docId || sidebarId || docsPluginId) ? [{
|
|
5946
|
-
label,
|
|
5947
|
-
...href ? { href } : {},
|
|
5948
|
-
...docId ? { docId } : {},
|
|
5949
|
-
...sidebarId ? { sidebarId } : {},
|
|
5950
|
-
...docsPluginId ? { docsPluginId } : {}
|
|
5951
|
-
}] : [];
|
|
6000
|
+
const announcement = objectValue3(config.announcement);
|
|
6001
|
+
const announcementMessage = typeof announcement?.message === "string" ? announcement.message.trim() : "";
|
|
6002
|
+
const bannerContent = announcementMarkdown(announcementMessage);
|
|
6003
|
+
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
6004
|
+
const redirect = objectValue3(value);
|
|
6005
|
+
if (!redirect || typeof redirect.source !== "string" || typeof redirect.destination !== "string") return [];
|
|
6006
|
+
const rawSource = redirect.source.trim();
|
|
6007
|
+
const rawDestination = redirect.destination.trim();
|
|
6008
|
+
if (!isRedirectPathSafe(rawSource, rawDestination)) return [];
|
|
6009
|
+
const translated = translateRedirectWildcards(rawSource, rawDestination);
|
|
6010
|
+
if (!translated) {
|
|
6011
|
+
warnOnce2(
|
|
6012
|
+
context,
|
|
6013
|
+
`fern-redirect-wildcard-${rawSource}`,
|
|
6014
|
+
`Redirect from ${rawSource} uses a wildcard Next.js cannot express and was dropped.`
|
|
6015
|
+
);
|
|
6016
|
+
return [];
|
|
6017
|
+
}
|
|
6018
|
+
return [{
|
|
6019
|
+
source: translated.source,
|
|
6020
|
+
destination: translated.destination,
|
|
6021
|
+
...typeof redirect.permanent === "boolean" ? { permanent: redirect.permanent } : {}
|
|
6022
|
+
}];
|
|
5952
6023
|
}) : [];
|
|
5953
|
-
const
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
6024
|
+
const authoredSources = new Set(redirects.map((redirect) => redirect.source));
|
|
6025
|
+
const knownIds = new Set(context.descriptors.map((descriptor) => descriptor.navigationId));
|
|
6026
|
+
const keptNavigationId = (descriptor) => context.sourcePathToNavigationId.get(descriptor.sourcePath) ?? descriptor.navigationId;
|
|
6027
|
+
const segmentAliasRedirects = context.segmentAliases.size > 0 ? context.descriptors.flatMap((descriptor) => {
|
|
6028
|
+
const segments = descriptor.navigationId.split("/");
|
|
6029
|
+
let changed = false;
|
|
6030
|
+
const aliased = segments.map((segment) => {
|
|
6031
|
+
const literal = context.segmentAliases.get(segment);
|
|
6032
|
+
if (!literal) return segment;
|
|
6033
|
+
changed = true;
|
|
6034
|
+
return literal;
|
|
5960
6035
|
});
|
|
5961
|
-
|
|
6036
|
+
if (!changed) return [];
|
|
6037
|
+
const source = `/${aliased.join("/")}`;
|
|
6038
|
+
const destination = `/${keptNavigationId(descriptor)}`;
|
|
6039
|
+
if (source === destination || knownIds.has(aliased.join("/"))) return [];
|
|
6040
|
+
return [{ source, destination }];
|
|
5962
6041
|
}) : [];
|
|
5963
|
-
const
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
...navbar && staticStringField(navbar, "title") ? { navbarTitle: staticStringField(navbar, "title") } : {},
|
|
5968
|
-
defaultColorMode,
|
|
5969
|
-
docsRouteBasePath,
|
|
5970
|
-
...logo && staticStringField(logo, "src") ? {
|
|
5971
|
-
logo: {
|
|
5972
|
-
light: staticStringField(logo, "src"),
|
|
5973
|
-
...staticStringField(logo, "srcDark") ? { dark: staticStringField(logo, "srcDark") } : {},
|
|
5974
|
-
showTitle: Boolean(navbar && staticStringField(navbar, "title"))
|
|
5975
|
-
}
|
|
5976
|
-
} : {},
|
|
5977
|
-
...staticStringField(source, "favicon") ? { favicon: staticStringField(source, "favicon") } : {},
|
|
5978
|
-
navbarLinks,
|
|
5979
|
-
footerLinks,
|
|
5980
|
-
...copyright ? { copyright } : {}
|
|
5981
|
-
};
|
|
5982
|
-
}
|
|
5983
|
-
function autogeneratedItems(dirName, context) {
|
|
5984
|
-
const normalizedDir = dirName === "." ? "" : normalizeDocId(dirName);
|
|
5985
|
-
const inDirectory = context.descriptors.filter((descriptor) => {
|
|
5986
|
-
const source = descriptor.sourcePath.replace(/\.(?:mdx?)$/i, "");
|
|
5987
|
-
return !normalizedDir || source === normalizedDir || source.startsWith(`${normalizedDir}/`);
|
|
6042
|
+
const duplicateNavRedirects = context.descriptors.flatMap((descriptor) => {
|
|
6043
|
+
const kept = keptNavigationId(descriptor);
|
|
6044
|
+
if (kept === descriptor.navigationId) return [];
|
|
6045
|
+
return [{ source: `/${descriptor.navigationId}`, destination: `/${kept}` }];
|
|
5988
6046
|
});
|
|
5989
|
-
const
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
5994
|
-
|
|
5995
|
-
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
// renders it as a child link, so put it before the category's other docs.
|
|
6001
|
-
position: descriptor.sidebarPosition ?? (/(?:^|\/)(?:index|readme)\.mdx?$/i.test(descriptor.sourcePath) ? -1 : Number.MAX_SAFE_INTEGER),
|
|
6002
|
-
sortKey: descriptor.sourcePath,
|
|
6003
|
-
item: descriptor.navigationId
|
|
6004
|
-
}));
|
|
6005
|
-
const groups = [...childDirectories].map((segment) => {
|
|
6006
|
-
const directory = normalizedDir ? posix3.join(normalizedDir, segment) : segment;
|
|
6007
|
-
const metadata = readCategoryMetadata(context.contentRoot, directory);
|
|
6008
|
-
const pages2 = autogeneratedItems(directory, context);
|
|
6009
|
-
if (pages2.length === 1 && typeof pages2[0] === "string" && !metadata.label && metadata.position === void 0 && !metadata.link) {
|
|
6010
|
-
const onlyPage = context.descriptors.find((descriptor) => descriptor.navigationId === pages2[0]);
|
|
6011
|
-
if (onlyPage && /(?:^|\/)(?:index|readme)\.mdx?$/i.test(onlyPage.sourcePath)) {
|
|
6012
|
-
return {
|
|
6013
|
-
position: onlyPage.sidebarPosition ?? Number.MAX_SAFE_INTEGER,
|
|
6014
|
-
sortKey: segment,
|
|
6015
|
-
item: pages2[0]
|
|
6016
|
-
};
|
|
6017
|
-
}
|
|
6047
|
+
const allRedirects = [
|
|
6048
|
+
...redirects,
|
|
6049
|
+
...context.bareRouteRedirects.filter((redirect) => !authoredSources.has(redirect.source)),
|
|
6050
|
+
...segmentAliasRedirects.filter((redirect) => !authoredSources.has(redirect.source)),
|
|
6051
|
+
...duplicateNavRedirects.filter((redirect) => !authoredSources.has(redirect.source))
|
|
6052
|
+
];
|
|
6053
|
+
const claimedTabLabels = /* @__PURE__ */ new Set();
|
|
6054
|
+
const disambiguatedApiSections = context.apiSections.map((section) => {
|
|
6055
|
+
if (!section.tabLabel || !claimedTabLabels.has(section.tabLabel)) {
|
|
6056
|
+
if (section.tabLabel) claimedTabLabels.add(section.tabLabel);
|
|
6057
|
+
return section;
|
|
6018
6058
|
}
|
|
6019
|
-
const
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6059
|
+
const distinguishingSegment = section.routeSegments.at(-1);
|
|
6060
|
+
let tabLabel = distinguishingSegment ? `${section.tabLabel}: ${titleCase2(distinguishingSegment)}` : section.tabLabel;
|
|
6061
|
+
if (!tabLabel || tabLabel === `${section.tabLabel}: ${section.tabLabel}` || claimedTabLabels.has(tabLabel)) {
|
|
6062
|
+
let ordinal = 2;
|
|
6063
|
+
let candidate = `${section.tabLabel} (${ordinal})`;
|
|
6064
|
+
while (claimedTabLabels.has(candidate)) {
|
|
6065
|
+
ordinal += 1;
|
|
6066
|
+
candidate = `${section.tabLabel} (${ordinal})`;
|
|
6027
6067
|
}
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
|
|
6032
|
-
return item;
|
|
6068
|
+
tabLabel = candidate;
|
|
6069
|
+
}
|
|
6070
|
+
claimedTabLabels.add(tabLabel);
|
|
6071
|
+
return { ...section, tabLabel };
|
|
6033
6072
|
});
|
|
6034
|
-
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
return autogeneratedItems(typeof object.dirName === "string" ? object.dirName : ".", context);
|
|
6049
|
-
}
|
|
6050
|
-
if (object.type === "category") {
|
|
6051
|
-
const label = typeof object.label === "string" ? object.label : "Documentation";
|
|
6052
|
-
const pages = convertItems(object.items, context);
|
|
6053
|
-
const landing = generatedIndexPage(label, object.link, context);
|
|
6054
|
-
if (landing && !pages.includes(landing)) pages.unshift(landing);
|
|
6055
|
-
return pages.length > 0 ? [{ group: label, pages }] : [];
|
|
6056
|
-
}
|
|
6057
|
-
if (object.type === "link" || object.type === "html") return [];
|
|
6058
|
-
return Object.entries(object).flatMap(([label, items]) => {
|
|
6059
|
-
const pages = convertItems(items, context);
|
|
6060
|
-
return pages.length > 0 ? [{ group: label, pages }] : [];
|
|
6061
|
-
});
|
|
6062
|
-
}
|
|
6063
|
-
function projectDocusaurusNavigation(input) {
|
|
6064
|
-
const warnings = [];
|
|
6065
|
-
const descriptorByDocId = new Map(input.descriptors.map((page) => [page.docId, page]));
|
|
6066
|
-
const context = {
|
|
6067
|
-
contentRoot: input.contentRoot,
|
|
6068
|
-
descriptors: input.descriptors,
|
|
6069
|
-
descriptorByDocId,
|
|
6070
|
-
generatedPages: [],
|
|
6071
|
-
generatedIds: /* @__PURE__ */ new Set(),
|
|
6072
|
-
referencedNavigationIds: /* @__PURE__ */ new Set(),
|
|
6073
|
-
sourceUrl: input.sourceUrl,
|
|
6074
|
-
routePrefix: input.routePrefix ?? "",
|
|
6075
|
-
sidebarSource: input.sidebars?.sourcePath ?? "autogenerated sidebar",
|
|
6076
|
-
warnings
|
|
6077
|
-
};
|
|
6078
|
-
const sidebarEntries = input.sidebars ? Object.entries(input.sidebars.config) : [["docs", [
|
|
6079
|
-
{ type: "autogenerated", dirName: "." }
|
|
6080
|
-
]]];
|
|
6081
|
-
const tabs = sidebarEntries.flatMap(([sidebarId, value]) => {
|
|
6082
|
-
const converted = convertItems(value, context);
|
|
6083
|
-
if (converted.length === 0) return [];
|
|
6084
|
-
return [{
|
|
6085
|
-
tab: sidebarEntries.length === 1 ? "Documentation" : titleCase2(sidebarId),
|
|
6086
|
-
// Docusaurus categories are disclosures, including at the sidebar
|
|
6087
|
-
// root. Keeping them as Thally root nodes preserves their collapsed
|
|
6088
|
-
// state and the authored interleaving of documents and categories.
|
|
6089
|
-
pages: converted
|
|
6090
|
-
}];
|
|
6091
|
-
});
|
|
6092
|
-
const unreferenced = input.descriptors.filter((page) => !context.referencedNavigationIds.has(page.navigationId)).sort(descriptorSort).map((page) => page.navigationId);
|
|
6093
|
-
if (unreferenced.length > 0) {
|
|
6094
|
-
if (tabs.length === 0) tabs.push({ tab: "Documentation", pages: [] });
|
|
6095
|
-
tabs[0].pages?.push({ group: "Additional", pages: unreferenced });
|
|
6096
|
-
}
|
|
6097
|
-
return {
|
|
6098
|
-
docsConfig: { tabs },
|
|
6099
|
-
generatedPages: context.generatedPages,
|
|
6100
|
-
referencedNavigationIds: context.referencedNavigationIds,
|
|
6101
|
-
warnings
|
|
6102
|
-
};
|
|
6073
|
+
return {
|
|
6074
|
+
navigationIncomplete: context.navigationWalk.incomplete,
|
|
6075
|
+
docsConfig: {
|
|
6076
|
+
tabs,
|
|
6077
|
+
...bannerContent ? { banner: { content: bannerContent, dismissible: true } } : {},
|
|
6078
|
+
...navbarLinks.length > 0 ? { navbar: { links: navbarLinks } } : {},
|
|
6079
|
+
...allRedirects.length > 0 ? { redirects: allRedirects } : {}
|
|
6080
|
+
},
|
|
6081
|
+
descriptors: context.descriptors,
|
|
6082
|
+
warnings: context.warnings,
|
|
6083
|
+
apiSections: disambiguatedApiSections,
|
|
6084
|
+
restrictedApiSections: context.restrictedApiSections,
|
|
6085
|
+
changelogIndexes: context.changelogIndexes
|
|
6086
|
+
};
|
|
6103
6087
|
}
|
|
6104
6088
|
|
|
6105
|
-
// src/
|
|
6106
|
-
import
|
|
6107
|
-
import
|
|
6108
|
-
import { load } from "cheerio";
|
|
6089
|
+
// src/docusaurus.ts
|
|
6090
|
+
import JSON5 from "json5";
|
|
6091
|
+
import ts4 from "typescript";
|
|
6109
6092
|
import { parse as parseYaml3 } from "yaml";
|
|
6110
6093
|
var MAX_CONFIG_BYTES2 = 2e7;
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
|
|
6121
|
-
if (/^https?:\/\//i.test(rawHref)) {
|
|
6122
|
-
try {
|
|
6123
|
-
href = new URL(rawHref).toString();
|
|
6124
|
-
} catch {
|
|
6125
|
-
}
|
|
6126
|
-
} else if (rawHref.startsWith("/") && !rawHref.startsWith("//") && !/[\\<>\s]/.test(rawHref)) {
|
|
6127
|
-
href = rawHref;
|
|
6128
|
-
}
|
|
6129
|
-
const destination = href?.replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
6130
|
-
links.set(marker, destination && label ? `[${escapeText(label)}](${destination})` : escapeText(label));
|
|
6131
|
-
document(element).replaceWith(marker);
|
|
6132
|
-
});
|
|
6133
|
-
let text2 = escapeText(document("body").text());
|
|
6134
|
-
for (const [marker, link] of links) text2 = text2.replaceAll(marker, link);
|
|
6135
|
-
return text2.trim();
|
|
6094
|
+
var CATEGORY_FILENAMES = ["_category_.json", "_category_.yml", "_category_.yaml"];
|
|
6095
|
+
var SIDEBAR_FILENAMES = [
|
|
6096
|
+
"sidebars.json",
|
|
6097
|
+
"sidebars.js",
|
|
6098
|
+
"sidebars.cjs",
|
|
6099
|
+
"sidebars.mjs",
|
|
6100
|
+
"sidebars.ts"
|
|
6101
|
+
];
|
|
6102
|
+
function escapeRegExp(value) {
|
|
6103
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6136
6104
|
}
|
|
6137
6105
|
function objectValue4(value) {
|
|
6138
6106
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
6139
6107
|
}
|
|
6140
|
-
function
|
|
6141
|
-
return value.
|
|
6108
|
+
function stripNumberPrefix(value) {
|
|
6109
|
+
return value.replace(/^\d+[-_]+/, "");
|
|
6142
6110
|
}
|
|
6143
|
-
function
|
|
6144
|
-
|
|
6145
|
-
|
|
6111
|
+
function normalizeDocId(value) {
|
|
6112
|
+
return value.split(/[?#]/, 1)[0].replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.(?:mdx?)$/i, "").split("/").filter(Boolean).map(stripNumberPrefix).join("/");
|
|
6113
|
+
}
|
|
6114
|
+
function docusaurusSlugifySegment(value) {
|
|
6115
|
+
let decoded = value;
|
|
6116
|
+
try {
|
|
6117
|
+
decoded = decodeURIComponent(value);
|
|
6118
|
+
} catch {
|
|
6146
6119
|
}
|
|
6147
|
-
|
|
6120
|
+
const slug2 = decoded.replace(/\.(?:html?|mdx?)$/i, "").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/(^-|-$)/g, "");
|
|
6121
|
+
return slug2 === "." || slug2 === ".." ? "" : slug2;
|
|
6148
6122
|
}
|
|
6149
|
-
function
|
|
6150
|
-
const
|
|
6151
|
-
|
|
6152
|
-
if (!existsSync4(path) || !lstatSync4(path).isFile()) return null;
|
|
6153
|
-
const config = objectValue4(readBoundedYaml(path));
|
|
6154
|
-
if (!config) throw new Error("fern/docs.yml must contain an object.");
|
|
6155
|
-
return { config, sourcePath };
|
|
6123
|
+
function docusaurusRouteId(value) {
|
|
6124
|
+
const segments = normalizeDocId(value).split("/").map(docusaurusSlugifySegment).filter(Boolean);
|
|
6125
|
+
return segments.join("/") || "introduction";
|
|
6156
6126
|
}
|
|
6157
|
-
function
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6127
|
+
function defaultDocusaurusRouteId(value) {
|
|
6128
|
+
const segments = normalizeDocId(value).split("/").filter(Boolean);
|
|
6129
|
+
if (/^(?:index|readme)$/i.test(segments.at(-1) ?? "")) segments.pop();
|
|
6130
|
+
const normalized = segments.map(docusaurusSlugifySegment).filter(Boolean);
|
|
6131
|
+
return normalized.join("/") || "introduction";
|
|
6161
6132
|
}
|
|
6162
|
-
function
|
|
6163
|
-
|
|
6164
|
-
if (typeof node === "string") return node;
|
|
6165
|
-
const nested = firstLeafId(node.pages);
|
|
6166
|
-
if (nested) return nested;
|
|
6167
|
-
}
|
|
6168
|
-
return void 0;
|
|
6133
|
+
function storageIdForRoute(routeId) {
|
|
6134
|
+
return /(?:^|\/)(?:index|readme)$/i.test(routeId) ? `${routeId}/index` : routeId;
|
|
6169
6135
|
}
|
|
6170
|
-
function
|
|
6171
|
-
|
|
6172
|
-
const destination = firstLeafId(pages);
|
|
6173
|
-
const source = segments.join("/");
|
|
6174
|
-
if (destination && destination !== source) {
|
|
6175
|
-
context.bareRouteRedirects.push({ source: `/${source}`, destination: `/${destination}` });
|
|
6176
|
-
}
|
|
6136
|
+
function titleCase3(value) {
|
|
6137
|
+
return stripNumberPrefix(value).replace(/sidebar$/i, "").split(/[-_]/).filter(Boolean).map((word) => ["api", "cli", "sdk", "ui"].includes(word.toLowerCase()) ? word.toUpperCase() : word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
6177
6138
|
}
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6139
|
+
function routeFromSlug(slug2, fallback, sourceDirectory) {
|
|
6140
|
+
if (slug2 === "/") return "introduction";
|
|
6141
|
+
if (slug2.startsWith("/")) return docusaurusRouteId(slug2);
|
|
6142
|
+
const fallbackDirectory = sourceDirectory ?? (fallback === "introduction" ? "" : posix3.dirname(fallback));
|
|
6143
|
+
const resolved = posix3.normalize(posix3.join(fallbackDirectory, slug2));
|
|
6144
|
+
if (resolved === ".." || resolved.startsWith("../")) return null;
|
|
6145
|
+
return docusaurusRouteId(resolved);
|
|
6181
6146
|
}
|
|
6182
|
-
function
|
|
6183
|
-
const
|
|
6184
|
-
|
|
6147
|
+
function resolveDocusaurusPageIdentity(sourcePath, frontmatter, fallback) {
|
|
6148
|
+
const sourceDocId = normalizeDocId(sourcePath);
|
|
6149
|
+
const sourceDirectory = posix3.dirname(sourceDocId);
|
|
6150
|
+
const configuredId = typeof frontmatter.id === "string" && frontmatter.id.trim() ? stripNumberPrefix(frontmatter.id.trim()) : null;
|
|
6151
|
+
const docId = configuredId ? normalizeDocId(sourceDirectory === "." ? configuredId : posix3.join(sourceDirectory, configuredId)) : sourceDocId;
|
|
6152
|
+
const defaultNavigationId = defaultDocusaurusRouteId(docId) ?? fallback.navigationId;
|
|
6153
|
+
const configuredSlug = typeof frontmatter.slug === "string" ? frontmatter.slug.trim() : "";
|
|
6154
|
+
const navigationId = configuredSlug ? routeFromSlug(
|
|
6155
|
+
configuredSlug,
|
|
6156
|
+
defaultNavigationId,
|
|
6157
|
+
sourceDirectory === "." ? "" : sourceDirectory
|
|
6158
|
+
) ?? defaultNavigationId : defaultNavigationId;
|
|
6159
|
+
const position = typeof frontmatter.sidebar_position === "number" && Number.isFinite(frontmatter.sidebar_position) ? frontmatter.sidebar_position : void 0;
|
|
6160
|
+
return {
|
|
6161
|
+
identity: { id: storageIdForRoute(navigationId), navigationId },
|
|
6162
|
+
descriptor: {
|
|
6163
|
+
sourcePath: sourcePath.replace(/\\/g, "/"),
|
|
6164
|
+
docId,
|
|
6165
|
+
navigationId,
|
|
6166
|
+
...position === void 0 ? {} : { sidebarPosition: position },
|
|
6167
|
+
...isUnlisted(frontmatter.unlisted) ? { unlisted: true } : {}
|
|
6168
|
+
}
|
|
6169
|
+
};
|
|
6185
6170
|
}
|
|
6186
|
-
function
|
|
6187
|
-
|
|
6188
|
-
if (typeof value.slug !== "string") return fernLabelSlug(label) || null;
|
|
6189
|
-
const explicit = value.slug.trim();
|
|
6190
|
-
if (!explicit) return null;
|
|
6191
|
-
const slugified = fernBasicSlug(explicit);
|
|
6192
|
-
if (context && explicit !== slugified && FERN_SEGMENT_ALIAS.test(explicit)) {
|
|
6193
|
-
context.segmentAliases.set(slugified, explicit);
|
|
6194
|
-
}
|
|
6195
|
-
return slugified || null;
|
|
6171
|
+
function isUnlisted(value) {
|
|
6172
|
+
return value !== void 0 && value !== null && value !== false && !(typeof value === "string" && ["", "false"].includes(value.trim().toLowerCase()));
|
|
6196
6173
|
}
|
|
6197
|
-
function
|
|
6198
|
-
|
|
6199
|
-
let index = 2;
|
|
6200
|
-
while (context.seenNavigationIds.has(`${base}-${index}`)) index++;
|
|
6201
|
-
return `${base}-${index}`;
|
|
6174
|
+
function sourceReferenceKey(value) {
|
|
6175
|
+
return value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.(?:mdx?)$/i, "").replace(/\/$/, "");
|
|
6202
6176
|
}
|
|
6203
|
-
function
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
)
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
return "";
|
|
6215
|
-
}
|
|
6216
|
-
const navigationId = uniqueNavigationId(base || "introduction", context);
|
|
6217
|
-
context.seenNavigationIds.add(navigationId);
|
|
6218
|
-
const isHidden = hidden || context.hiddenInherited === true;
|
|
6219
|
-
context.descriptors.push({ sourcePath, navigationId, ...navTitle ? { navTitle } : {}, ...isHidden ? { hidden: true } : {} });
|
|
6220
|
-
const kept = context.sourcePathToNavigationId.get(sourcePath);
|
|
6221
|
-
if (kept) {
|
|
6222
|
-
if (!isHidden) {
|
|
6223
|
-
const owner = context.descriptors.find((descriptor) => descriptor.navigationId === kept);
|
|
6224
|
-
if (owner) owner.hidden = false;
|
|
6177
|
+
function sourceHeadings(body) {
|
|
6178
|
+
const headings = [];
|
|
6179
|
+
const lines = body.split("\n");
|
|
6180
|
+
let fence = null;
|
|
6181
|
+
for (let index = 0; index < lines.length; index++) {
|
|
6182
|
+
const line = lines[index];
|
|
6183
|
+
const marker = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
|
6184
|
+
if (marker) {
|
|
6185
|
+
if (!fence) fence = marker[1];
|
|
6186
|
+
else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && /^\s*$/.test(line.slice(marker[0].length))) fence = null;
|
|
6187
|
+
continue;
|
|
6225
6188
|
}
|
|
6226
|
-
|
|
6189
|
+
if (fence) continue;
|
|
6190
|
+
const heading = line.match(/^ {0,3}(#{2,6})\s+(.+?)\s*#*\s*$/);
|
|
6191
|
+
if (!heading) continue;
|
|
6192
|
+
const preceding = lines[index - 1]?.match(/^<a id="([\p{L}\p{N}_.:$-]{1,120})"><\/a>$/u);
|
|
6193
|
+
const plainText2 = !/[<>{}\[\]`*_]/.test(heading[2]);
|
|
6194
|
+
const generatedId = plainText2 ? heading[2].normalize("NFC").toLowerCase().replace(/[^\p{L}\p{M}\p{N}]+/gu, "-").replace(/(^-|-$)/g, "") : null;
|
|
6195
|
+
headings.push({ index, level: heading[1].length, id: preceding?.[1] ?? generatedId });
|
|
6227
6196
|
}
|
|
6228
|
-
|
|
6229
|
-
return navigationId;
|
|
6197
|
+
return headings;
|
|
6230
6198
|
}
|
|
6231
|
-
function
|
|
6232
|
-
const
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
);
|
|
6240
|
-
|
|
6241
|
-
|
|
6242
|
-
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
const label = typeof object.title === "string" && object.title.trim() ? object.title.trim() : titleCase3(basename2(folder));
|
|
6249
|
-
const segment = typeof object.slug === "string" || object["skip-slug"] === true ? segmentFor(object, label, context) : fernBasicSlug(basename2(folder));
|
|
6250
|
-
const segments = segment ? [...parentSegments, segment] : parentSegments;
|
|
6251
|
-
const visit = (directory, routeSegments, depth) => {
|
|
6252
|
-
if (depth > 12 || context.descriptors.length >= 5e3) return [];
|
|
6253
|
-
const pages2 = [];
|
|
6254
|
-
const entries = readdirSync(directory, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".") && !entry.isSymbolicLink()).sort((left, right) => Number(left.name !== "index.mdx" && left.name !== "index.md") - Number(right.name !== "index.mdx" && right.name !== "index.md") || left.name.localeCompare(right.name));
|
|
6255
|
-
for (const entry of entries) {
|
|
6256
|
-
const path = resolveWithin(directory, entry.name);
|
|
6257
|
-
if (entry.isDirectory()) {
|
|
6258
|
-
const nested = visit(path, [...routeSegments, fernBasicSlug(entry.name)], depth + 1);
|
|
6259
|
-
if (nested.length) pages2.push({ group: titleCase3(entry.name), pages: nested });
|
|
6260
|
-
} else if (entry.isFile() && [".md", ".mdx"].includes(extname5(entry.name).toLowerCase())) {
|
|
6261
|
-
const stem = basename2(entry.name, extname5(entry.name));
|
|
6262
|
-
const pageSegment = stem.toLowerCase() === "index" ? null : fernBasicSlug(stem);
|
|
6263
|
-
const route = [...routeSegments, ...pageSegment ? [pageSegment] : []].join("/");
|
|
6264
|
-
const source = lstatSync4(path).size <= MAX_CONFIG_BYTES2 ? readFileSync4(path, "utf8") : "";
|
|
6265
|
-
const frontmatter = source.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1] ?? "";
|
|
6266
|
-
const hidden = /^hidden:\s*true\s*$/m.test(frontmatter);
|
|
6267
|
-
const registered = registerPageAt(relative4(context.fernRoot, path), route, { ...context, pathPrefix: "" }, void 0, hidden);
|
|
6268
|
-
if (registered && !hidden && !context.hiddenInherited) pages2.push(registered);
|
|
6269
|
-
}
|
|
6199
|
+
function addDocusaurusTranslatedHeadingAliases(pages) {
|
|
6200
|
+
const byRoute = new Map(pages.map((page) => [page.navigationId, page]));
|
|
6201
|
+
for (const page of pages) {
|
|
6202
|
+
const locale = page.source.match(/(?:^|[/#])i18n\/([A-Za-z]{2,8}(?:-[A-Za-z0-9]{2,8})*)\/docusaurus-plugin-content-docs\//)?.[1];
|
|
6203
|
+
if (!locale) continue;
|
|
6204
|
+
const originalRoute = page.navigationId === locale ? "introduction" : page.navigationId.startsWith(`${locale}/`) ? page.navigationId.slice(locale.length + 1) : null;
|
|
6205
|
+
if (!originalRoute) continue;
|
|
6206
|
+
const original = byRoute.get(originalRoute);
|
|
6207
|
+
if (!original) continue;
|
|
6208
|
+
const originalHeadings = sourceHeadings(original.body);
|
|
6209
|
+
const translatedHeadings = sourceHeadings(page.body);
|
|
6210
|
+
if (!originalHeadings.length || originalHeadings.length !== translatedHeadings.length || originalHeadings.some((heading, index) => heading.level !== translatedHeadings[index].level)) continue;
|
|
6211
|
+
const occupied = new Set(translatedHeadings.map((heading) => heading.id).filter((id) => !!id));
|
|
6212
|
+
for (const match of page.body.matchAll(/<a\s+id="([^"]+)"/g)) occupied.add(match[1]);
|
|
6213
|
+
const originalIdCounts = /* @__PURE__ */ new Map();
|
|
6214
|
+
for (const heading of originalHeadings) {
|
|
6215
|
+
if (heading.id) originalIdCounts.set(heading.id, (originalIdCounts.get(heading.id) ?? 0) + 1);
|
|
6270
6216
|
}
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
}
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
const base = [...parentSegments, segment].filter(Boolean).join("/");
|
|
6283
|
-
return registerPageAt(object.path, base, context, label, object.hidden === true) || null;
|
|
6284
|
-
}
|
|
6285
|
-
function convertNode(node, parentSegments, context) {
|
|
6286
|
-
const object = objectValue4(node);
|
|
6287
|
-
if (!object) return null;
|
|
6288
|
-
if (typeof object.page === "string") {
|
|
6289
|
-
const page = registerPage(object, parentSegments, context);
|
|
6290
|
-
return object.hidden === true || context.hiddenInherited ? null : page;
|
|
6291
|
-
}
|
|
6292
|
-
if (typeof object.folder === "string") {
|
|
6293
|
-
const folder = registerFolder(object, parentSegments, object.hidden === true ? { ...context, hiddenInherited: true } : context);
|
|
6294
|
-
return object.hidden === true || context.hiddenInherited ? null : folder;
|
|
6217
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
6218
|
+
for (let index = 0; index < originalHeadings.length; index++) {
|
|
6219
|
+
const id = originalHeadings[index].id;
|
|
6220
|
+
if (!id || occupied.has(id) || originalIdCounts.get(id) !== 1) continue;
|
|
6221
|
+
aliases.set(translatedHeadings[index].index, id);
|
|
6222
|
+
occupied.add(id);
|
|
6223
|
+
}
|
|
6224
|
+
if (aliases.size) page.body = page.body.split("\n").flatMap((line, index) => {
|
|
6225
|
+
const alias = aliases.get(index);
|
|
6226
|
+
return alias ? [`<a id="${alias}"></a>`, line] : [line];
|
|
6227
|
+
}).join("\n");
|
|
6295
6228
|
}
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
const
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
const registered = registerPageAt(object.path, segments.join("/"), context, label, object.hidden === true);
|
|
6305
|
-
if (registered) pages.push(registered);
|
|
6229
|
+
}
|
|
6230
|
+
function expandDocusaurusDocCardLists(pages, config, sourceRoutes) {
|
|
6231
|
+
const pageByRoute = new Map(pages.map((page) => [page.navigationId, page]));
|
|
6232
|
+
const containsRoute = (items, route) => items.some((item) => typeof item === "string" ? item === route : containsRoute(item.pages, route));
|
|
6233
|
+
const findGroup = (items, route) => {
|
|
6234
|
+
for (const item of items) {
|
|
6235
|
+
if (typeof item === "string" || !containsRoute(item.pages, route)) continue;
|
|
6236
|
+
return findGroup(item.pages, route) ?? item;
|
|
6306
6237
|
}
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6238
|
+
return void 0;
|
|
6239
|
+
};
|
|
6240
|
+
const firstRoute = (items) => {
|
|
6241
|
+
for (const item of items) {
|
|
6242
|
+
if (typeof item === "string") return item;
|
|
6243
|
+
const candidate = firstRoute(item.pages);
|
|
6244
|
+
if (candidate) return candidate;
|
|
6310
6245
|
}
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
...
|
|
6317
|
-
...
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
const
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
if (["http:", "https:"].includes(url.protocol) && !url.username && !url.password) href = url.toString();
|
|
6329
|
-
} catch {
|
|
6246
|
+
return void 0;
|
|
6247
|
+
};
|
|
6248
|
+
for (const page of pages) {
|
|
6249
|
+
if (!sourceRoutes.has(page.navigationId)) continue;
|
|
6250
|
+
const group = config.tabs.map((tab) => findGroup([
|
|
6251
|
+
...tab.pages ?? [],
|
|
6252
|
+
...tab.groups ?? []
|
|
6253
|
+
], page.navigationId)).find(Boolean);
|
|
6254
|
+
if (!group) {
|
|
6255
|
+
page.body = page.body.replace(/<div data-thally-doc-card-list=""\s*\/>/g, "");
|
|
6256
|
+
continue;
|
|
6257
|
+
}
|
|
6258
|
+
const cards = group.pages.flatMap((item) => {
|
|
6259
|
+
if (typeof item === "string") {
|
|
6260
|
+
if (item === page.navigationId) return [];
|
|
6261
|
+
const target = pageByRoute.get(item);
|
|
6262
|
+
return [{ title: target?.title ?? item.split("/").at(-1) ?? item, route: item }];
|
|
6330
6263
|
}
|
|
6264
|
+
const slug2 = slugifySegment(item.group);
|
|
6265
|
+
const routes = [...item.pages].flatMap((child) => typeof child === "string" ? [child] : []);
|
|
6266
|
+
const route = routes.find((candidate) => candidate === slug2 || candidate.endsWith(`/${slug2}`)) ?? firstRoute(item.pages);
|
|
6267
|
+
return route ? [{ title: item.group, route }] : [];
|
|
6268
|
+
});
|
|
6269
|
+
const markup = cards.length ? `<CardGroup>
|
|
6270
|
+
${cards.map(({ title, route }) => ` <Card title={${JSON.stringify(title)}} href={${JSON.stringify(`/${route === "introduction" ? "" : route}`)}} />`).join("\n")}
|
|
6271
|
+
</CardGroup>` : "";
|
|
6272
|
+
page.body = page.body.replace(/<div data-thally-doc-card-list=""\s*\/>/g, markup);
|
|
6273
|
+
if (markup && !page.body.includes(markup)) page.body = [page.body.trim(), markup].filter(Boolean).join("\n\n");
|
|
6274
|
+
}
|
|
6275
|
+
}
|
|
6276
|
+
function rewriteDocusaurusLinks(body, current, descriptors, options = {}) {
|
|
6277
|
+
const routes = /* @__PURE__ */ new Map();
|
|
6278
|
+
for (const descriptor of descriptors) {
|
|
6279
|
+
const sourceKey = sourceReferenceKey(descriptor.sourcePath);
|
|
6280
|
+
routes.set(sourceKey, descriptor.navigationId);
|
|
6281
|
+
routes.set(sourceReferenceKey(descriptor.docId), descriptor.navigationId);
|
|
6282
|
+
routes.set(sourceReferenceKey(descriptor.navigationId), descriptor.navigationId);
|
|
6283
|
+
const sourceRoute = pageIdFromReference(sourceKey);
|
|
6284
|
+
if (sourceRoute) routes.set(sourceRoute, descriptor.navigationId);
|
|
6285
|
+
}
|
|
6286
|
+
const currentDirectory = posix3.dirname(sourceReferenceKey(current.sourcePath));
|
|
6287
|
+
function copiedAssetTarget(target) {
|
|
6288
|
+
if (!target.startsWith("/") || target.startsWith("//") || target.includes("\\")) return null;
|
|
6289
|
+
const suffixIndex = target.search(/[?#]/);
|
|
6290
|
+
const path = suffixIndex >= 0 ? target.slice(0, suffixIndex) : target;
|
|
6291
|
+
const suffix = suffixIndex >= 0 ? target.slice(suffixIndex) : "";
|
|
6292
|
+
if (path.startsWith("/docs/") && options.assetPaths?.has(path.slice("/docs/".length))) {
|
|
6293
|
+
return `/${path.slice("/docs/".length)}${suffix}`;
|
|
6331
6294
|
}
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6295
|
+
return options.assetPaths?.has(path.slice(1)) ? target : null;
|
|
6296
|
+
}
|
|
6297
|
+
function rewriteTarget(target) {
|
|
6298
|
+
if (!target || /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(target)) return target;
|
|
6299
|
+
if (target.startsWith("#")) return target;
|
|
6300
|
+
const suffixIndex = target.search(/[?#]/);
|
|
6301
|
+
const path = suffixIndex >= 0 ? target.slice(0, suffixIndex) : target;
|
|
6302
|
+
const suffix = suffixIndex >= 0 ? target.slice(suffixIndex) : "";
|
|
6303
|
+
const copiedAsset = copiedAssetTarget(target);
|
|
6304
|
+
if (copiedAsset) return copiedAsset;
|
|
6305
|
+
const candidates = path.startsWith("/") ? [
|
|
6306
|
+
sourceReferenceKey(path),
|
|
6307
|
+
sourceReferenceKey(path).replace(/^docs\//, ""),
|
|
6308
|
+
sourceReferenceKey(path).replace(/^docs\/(?:next|current|latest)\//, "")
|
|
6309
|
+
] : [
|
|
6310
|
+
sourceReferenceKey(posix3.normalize(posix3.join(currentDirectory, path))),
|
|
6311
|
+
sourceReferenceKey(path)
|
|
6312
|
+
];
|
|
6313
|
+
const route = candidates.map((candidate) => routes.get(candidate)).find(Boolean);
|
|
6314
|
+
if (!route) {
|
|
6315
|
+
if (path.startsWith("/") && path !== "/" && options.sourceOrigin) {
|
|
6316
|
+
options.onExternalLink?.(target);
|
|
6317
|
+
return new URL(target, options.sourceOrigin).toString();
|
|
6335
6318
|
}
|
|
6336
|
-
|
|
6337
|
-
warnOnce2(context, `fern-link-${rawHref}`, `Fern navigation link "${label}" has an unsafe or invalid external URL and was skipped.`);
|
|
6319
|
+
return target;
|
|
6338
6320
|
}
|
|
6339
|
-
return
|
|
6321
|
+
return `${route === "introduction" ? "/" : `/${route}`}${suffix}`;
|
|
6340
6322
|
}
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6323
|
+
let codeFence = null;
|
|
6324
|
+
return body.split("\n").map((line) => {
|
|
6325
|
+
const fence = line.match(/^\s{0,3}(`{3,}|~{3,})/);
|
|
6326
|
+
if (fence) {
|
|
6327
|
+
if (!codeFence) codeFence = fence[1];
|
|
6328
|
+
else if (fence[1][0] === codeFence[0] && fence[1].length >= codeFence.length && /^\s*$/.test(line.slice(fence[0].length))) codeFence = null;
|
|
6329
|
+
return line;
|
|
6330
|
+
}
|
|
6331
|
+
if (codeFence) return line;
|
|
6332
|
+
return line.replace(/(\]\()([^\s)]+)(?=[\s)]|$)/g, (_match, prefix, target) => `${prefix}${rewriteTarget(target)}`).replace(/(\bhref=")([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${rewriteTarget(target)}${suffix}`).replace(/(\bsrc=")([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${copiedAssetTarget(target) ?? target}${suffix}`).replace(/^(\s{0,3}\[[^\]]+\]:\s*)(<[^>]+>|\S+)/, (_match, prefix, destination) => {
|
|
6333
|
+
const bracketed = destination.startsWith("<") && destination.endsWith(">");
|
|
6334
|
+
const target = bracketed ? destination.slice(1, -1) : destination;
|
|
6335
|
+
const rewritten = rewriteTarget(target);
|
|
6336
|
+
return `${prefix}${bracketed ? `<${rewritten}>` : rewritten}`;
|
|
6347
6337
|
});
|
|
6348
|
-
|
|
6338
|
+
}).join("\n");
|
|
6339
|
+
}
|
|
6340
|
+
function readDocusaurusSiteOrigin(repositoryRoot) {
|
|
6341
|
+
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
6342
|
+
if (!configPath) return void 0;
|
|
6343
|
+
const address = readBoundedText(configPath).match(/\burl['"]?\s*:\s*(['"])(https?:\/\/[^'"]+)\1/)?.[2];
|
|
6344
|
+
if (!address) return void 0;
|
|
6345
|
+
try {
|
|
6346
|
+
return new URL(address).origin;
|
|
6347
|
+
} catch {
|
|
6348
|
+
return void 0;
|
|
6349
6349
|
}
|
|
6350
|
-
|
|
6351
|
-
|
|
6350
|
+
}
|
|
6351
|
+
function readBoundedText(path) {
|
|
6352
|
+
if (lstatSync4(path).size > MAX_CONFIG_BYTES2) {
|
|
6353
|
+
throw new Error("The Docusaurus sidebar config is larger than 20 MB and could not be imported.");
|
|
6352
6354
|
}
|
|
6353
|
-
return
|
|
6355
|
+
return readFileSync4(path, "utf8");
|
|
6354
6356
|
}
|
|
6355
|
-
function
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6357
|
+
function braceTable(source) {
|
|
6358
|
+
const table = /* @__PURE__ */ new Map();
|
|
6359
|
+
try {
|
|
6360
|
+
const tree = ts4.createSourceFile("config.ts", source, ts4.ScriptTarget.Latest, false, ts4.ScriptKind.TS);
|
|
6361
|
+
const pending = [tree];
|
|
6362
|
+
while (pending.length > 0) {
|
|
6363
|
+
const node = pending.pop();
|
|
6364
|
+
if (ts4.isObjectLiteralExpression(node) || ts4.isBlock(node)) {
|
|
6365
|
+
const start = node.getStart(tree);
|
|
6366
|
+
const end = node.end - 1;
|
|
6367
|
+
if (source[start] === "{" && source[end] === "}") table.set(start, end);
|
|
6368
|
+
}
|
|
6369
|
+
ts4.forEachChild(node, (child) => {
|
|
6370
|
+
pending.push(child);
|
|
6371
|
+
});
|
|
6372
|
+
}
|
|
6373
|
+
} catch {
|
|
6374
|
+
}
|
|
6375
|
+
return table;
|
|
6360
6376
|
}
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6377
|
+
var lastBraceSource;
|
|
6378
|
+
var lastBraceTable = /* @__PURE__ */ new Map();
|
|
6379
|
+
function matchingObjectLiteral(source, start) {
|
|
6380
|
+
let index = start;
|
|
6381
|
+
while (index < source.length) {
|
|
6382
|
+
if (/\s/.test(source[index])) index++;
|
|
6383
|
+
else if (source.startsWith("//", index)) {
|
|
6384
|
+
const end2 = source.indexOf("\n", index);
|
|
6385
|
+
index = end2 < 0 ? source.length : end2 + 1;
|
|
6386
|
+
} else if (source.startsWith("/*", index)) {
|
|
6387
|
+
const end2 = source.indexOf("*/", index + 2);
|
|
6388
|
+
index = end2 < 0 ? source.length : end2 + 2;
|
|
6389
|
+
} else break;
|
|
6390
|
+
}
|
|
6391
|
+
if (source[index] !== "{") return null;
|
|
6392
|
+
if (lastBraceSource !== source) {
|
|
6393
|
+
lastBraceSource = source;
|
|
6394
|
+
lastBraceTable = braceTable(source);
|
|
6395
|
+
}
|
|
6396
|
+
const end = lastBraceTable.get(index);
|
|
6397
|
+
return end === void 0 ? null : source.slice(index, end + 1);
|
|
6365
6398
|
}
|
|
6366
|
-
function
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6380
|
-
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6384
|
-
|
|
6385
|
-
|
|
6386
|
-
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6391
|
-
|
|
6399
|
+
function parseStaticSidebarModule(source) {
|
|
6400
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
6401
|
+
const normalizedSource = replaceExternalFbContent(source);
|
|
6402
|
+
const parseFailures = [];
|
|
6403
|
+
const assignmentPatterns = [
|
|
6404
|
+
/\bmodule\.exports\s*=\s*/g,
|
|
6405
|
+
/\bexport\s+default\s*/g,
|
|
6406
|
+
/\b(?:const|let|var)\s+[A-Za-z_$][\w$]*(?:\s*:\s*(?:(?!\b(?:const|let|var)\b)[^=;])+)?\s*=\s*/g
|
|
6407
|
+
];
|
|
6408
|
+
const candidates = assignmentPatterns.flatMap((pattern) => [...normalizedSource.matchAll(pattern)]).sort((left, right) => (left.index ?? 0) - (right.index ?? 0));
|
|
6409
|
+
for (const candidate of candidates) {
|
|
6410
|
+
const literal = matchingObjectLiteral(normalizedSource, (candidate.index ?? 0) + candidate[0].length);
|
|
6411
|
+
if (!literal) continue;
|
|
6412
|
+
try {
|
|
6413
|
+
const substituted = [...bindings].reduce(
|
|
6414
|
+
(value, [name, binding]) => value.replace(
|
|
6415
|
+
new RegExp(`(:\\s*)${escapeRegExp(name)}(?![\\w$])`, "g"),
|
|
6416
|
+
(_match, prefix) => `${prefix}${JSON.stringify(binding)}`
|
|
6417
|
+
),
|
|
6418
|
+
literal
|
|
6419
|
+
);
|
|
6420
|
+
const parsed = JSON5.parse(substituted);
|
|
6421
|
+
const object = objectValue4(parsed);
|
|
6422
|
+
if (!object) continue;
|
|
6423
|
+
const bindingName = candidate[0].match(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/)?.[1];
|
|
6424
|
+
if (bindingName) {
|
|
6425
|
+
bindings.set(bindingName, object);
|
|
6426
|
+
continue;
|
|
6427
|
+
}
|
|
6428
|
+
return object;
|
|
6429
|
+
} catch (error) {
|
|
6430
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6431
|
+
const line = Number(message.match(/at (\d+):/)?.[1] ?? 0);
|
|
6432
|
+
const context = line > 0 ? literal.split("\n").slice(Math.max(0, line - 2), line + 1).join(" ") : "";
|
|
6433
|
+
parseFailures.push(`${message}${context ? ` near ${context}` : ""}`);
|
|
6392
6434
|
}
|
|
6393
|
-
const versionConfig = objectValue4(readBoundedYaml(versionPath));
|
|
6394
|
-
if (!versionConfig) return config;
|
|
6395
|
-
context.pathPrefix = relative4(fernRoot, dirname3(versionPath)).replace(/\\/g, "/");
|
|
6396
|
-
return { ...config, navigation: versionConfig.navigation, tabs: versionConfig.tabs ?? config.tabs };
|
|
6397
|
-
} catch (error) {
|
|
6398
|
-
warnOnce2(
|
|
6399
|
-
context,
|
|
6400
|
-
"fern-version-read-failed",
|
|
6401
|
-
`Fern version file "${chosen.path}" could not be read (${error instanceof Error ? error.message : String(error)}) and was skipped.`
|
|
6402
|
-
);
|
|
6403
|
-
return config;
|
|
6404
6435
|
}
|
|
6436
|
+
const exportedBinding = normalizedSource.match(/\bexport\s+default\s+([A-Za-z_$][\w$]*)\b/)?.[1] ?? normalizedSource.match(/\bmodule\.exports\s*=\s*([A-Za-z_$][\w$]*)\b/)?.[1];
|
|
6437
|
+
if (exportedBinding && bindings.has(exportedBinding)) return bindings.get(exportedBinding);
|
|
6438
|
+
throw new Error(`Sidebar config is executable or outside the supported data-only syntax.${parseFailures[0] ? ` ${parseFailures[0]}` : ""}`);
|
|
6405
6439
|
}
|
|
6406
|
-
function
|
|
6407
|
-
const
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
|
|
6412
|
-
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
|
|
6418
|
-
|
|
6419
|
-
|
|
6420
|
-
|
|
6421
|
-
|
|
6422
|
-
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
if (
|
|
6428
|
-
const
|
|
6429
|
-
if (
|
|
6430
|
-
const indexRoute = segments.join("/");
|
|
6431
|
-
const descriptors = new Map(context.descriptors.map((descriptor) => [descriptor.navigationId, descriptor]));
|
|
6432
|
-
const visiblePages = changelog.pages.filter((page) => typeof page === "string").filter((page) => !descriptors.get(page)?.hidden);
|
|
6433
|
-
const index = visiblePages.find((page) => {
|
|
6434
|
-
const descriptor = descriptors.get(page);
|
|
6435
|
-
if (!descriptor) return false;
|
|
6436
|
-
const stem = basename2(descriptor.sourcePath, extname5(descriptor.sourcePath));
|
|
6437
|
-
return page === indexRoute || stem === "overview" || stem === "index";
|
|
6438
|
-
});
|
|
6439
|
-
const entries = visiblePages.filter((page) => page !== index).sort((left, right) => {
|
|
6440
|
-
const date = (page) => descriptors.get(page)?.sourcePath.match(/\d{4}-\d{2}-\d{2}/)?.[0] ?? "";
|
|
6441
|
-
return date(right).localeCompare(date(left)) || left.localeCompare(right);
|
|
6442
|
-
});
|
|
6443
|
-
for (const entryId of entries) {
|
|
6444
|
-
const descriptor = descriptors.get(entryId);
|
|
6445
|
-
if (descriptor) descriptor.hidden = true;
|
|
6446
|
-
}
|
|
6447
|
-
changelog.pages = [index ?? indexRoute];
|
|
6448
|
-
context.changelogIndexes.push({ route: indexRoute, entries });
|
|
6449
|
-
const latest = index ?? firstLeafId(changelog.pages);
|
|
6450
|
-
const bareRouteIndex = context.bareRouteRedirects.findIndex((redirect) => redirect.source === `/${indexRoute}`);
|
|
6451
|
-
const indexDescriptor = index ? descriptors.get(index) : void 0;
|
|
6452
|
-
const indexSource = indexDescriptor && lstatSync4(resolveWithinRoot(context.fernRoot, indexDescriptor.sourcePath, context.repositoryRoot)).size <= MAX_CONFIG_BYTES2 ? readFileSync4(resolveWithinRoot(context.fernRoot, indexDescriptor.sourcePath, context.repositoryRoot), "utf8") : "";
|
|
6453
|
-
const indexFrontmatter = indexSource.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1] ?? "";
|
|
6454
|
-
const indexSlug = indexFrontmatter.match(/^slug:\s*["']?([^\s"']+)["']?\s*$/m)?.[1]?.replace(/^\/+|\/+$/g, "");
|
|
6455
|
-
const hasBarePage = index === indexRoute || indexSlug === indexRoute;
|
|
6456
|
-
if (bareRouteIndex >= 0 && hasBarePage) context.bareRouteRedirects.splice(bareRouteIndex, 1);
|
|
6457
|
-
else if (latest && bareRouteIndex >= 0) context.bareRouteRedirects[bareRouteIndex].destination = `/${latest}`;
|
|
6458
|
-
return [{ tab: label, groups: [changelog] }];
|
|
6459
|
-
}
|
|
6440
|
+
function replaceExternalFbContent(source) {
|
|
6441
|
+
const marker = "...fbContent(";
|
|
6442
|
+
let output = "";
|
|
6443
|
+
let cursor = 0;
|
|
6444
|
+
let searchFrom = 0;
|
|
6445
|
+
while (true) {
|
|
6446
|
+
const start = source.indexOf(marker, searchFrom);
|
|
6447
|
+
if (start < 0) return `${output}${source.slice(cursor)}`.replace(/,\s*,/g, ",");
|
|
6448
|
+
const objectStart = source.indexOf("{", start + marker.length);
|
|
6449
|
+
if (objectStart < 0) return `${output}${source.slice(cursor)}`;
|
|
6450
|
+
const objectLiteral = matchingObjectLiteral(source, objectStart);
|
|
6451
|
+
if (!objectLiteral) {
|
|
6452
|
+
searchFrom = start + marker.length;
|
|
6453
|
+
continue;
|
|
6454
|
+
}
|
|
6455
|
+
const close = source.indexOf(")", objectStart + objectLiteral.length);
|
|
6456
|
+
if (close < 0) return `${output}${source.slice(cursor)}`;
|
|
6457
|
+
const externalMatch = /\bexternal\s*:\s*/g.exec(objectLiteral);
|
|
6458
|
+
let replacement = "";
|
|
6459
|
+
if (externalMatch) {
|
|
6460
|
+
const arrayStart = objectLiteral.indexOf("[", externalMatch.index + externalMatch[0].length);
|
|
6461
|
+
if (arrayStart >= 0) {
|
|
6462
|
+
const array = matchingArrayLiteral(objectLiteral, arrayStart);
|
|
6463
|
+
if (array) replacement = array.slice(1, -1);
|
|
6460
6464
|
}
|
|
6461
|
-
if (groups.length === 0) return [];
|
|
6462
|
-
if (segments.length > 0) addBareRouteRedirect(segments, groups, context);
|
|
6463
|
-
return [{
|
|
6464
|
-
tab: label,
|
|
6465
|
-
...typeof meta.icon === "string" ? { icon: meta.icon } : {},
|
|
6466
|
-
groups
|
|
6467
|
-
}];
|
|
6468
|
-
});
|
|
6469
|
-
}
|
|
6470
|
-
if (Array.isArray(navigation)) {
|
|
6471
|
-
const sectionsBefore = context.apiSections.length;
|
|
6472
|
-
const groups = groupsFromConverted(convertNodes(navigation, routePrefix, context));
|
|
6473
|
-
for (let index = sectionsBefore; index < context.apiSections.length; index += 1) {
|
|
6474
|
-
context.apiSections[index].tabLabel ??= fallbackTabLabel;
|
|
6475
6465
|
}
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6466
|
+
let replaceEnd = close + 1;
|
|
6467
|
+
if (!replacement) {
|
|
6468
|
+
const trailingComma = /\s*,/y;
|
|
6469
|
+
trailingComma.lastIndex = replaceEnd;
|
|
6470
|
+
const found = trailingComma.exec(source);
|
|
6471
|
+
if (found) replaceEnd += found[0].length;
|
|
6479
6472
|
}
|
|
6473
|
+
output += `${source.slice(cursor, start)}${replacement}`;
|
|
6474
|
+
cursor = replaceEnd;
|
|
6475
|
+
searchFrom = replaceEnd;
|
|
6480
6476
|
}
|
|
6481
|
-
return [];
|
|
6482
6477
|
}
|
|
6483
|
-
function
|
|
6484
|
-
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
const
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
return [];
|
|
6478
|
+
function matchingArrayLiteral(source, start) {
|
|
6479
|
+
let depth = 0;
|
|
6480
|
+
let quote = "";
|
|
6481
|
+
let isEscaped = false;
|
|
6482
|
+
for (let index = start; index < source.length; index++) {
|
|
6483
|
+
const character = source[index];
|
|
6484
|
+
if (quote) {
|
|
6485
|
+
if (isEscaped) isEscaped = false;
|
|
6486
|
+
else if (character === "\\") isEscaped = true;
|
|
6487
|
+
else if (character === quote) quote = "";
|
|
6488
|
+
continue;
|
|
6495
6489
|
}
|
|
6496
|
-
if (
|
|
6497
|
-
|
|
6498
|
-
return
|
|
6490
|
+
if (source.startsWith("//", index)) {
|
|
6491
|
+
const end = source.indexOf("\n", index + 2);
|
|
6492
|
+
if (end < 0) return null;
|
|
6493
|
+
index = end;
|
|
6494
|
+
continue;
|
|
6499
6495
|
}
|
|
6500
|
-
|
|
6501
|
-
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6505
|
-
context,
|
|
6506
|
-
`fern-product-read-failed-${rawPath}`,
|
|
6507
|
-
`Fern product "${label}" could not be read (${error instanceof Error ? error.message : String(error)}) and was skipped.`
|
|
6508
|
-
);
|
|
6509
|
-
return [];
|
|
6496
|
+
if (source.startsWith("/*", index)) {
|
|
6497
|
+
const end = source.indexOf("*/", index + 2);
|
|
6498
|
+
if (end < 0) return null;
|
|
6499
|
+
index = end + 1;
|
|
6500
|
+
continue;
|
|
6510
6501
|
}
|
|
6511
|
-
if (
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
const priorPrefix = context.pathPrefix;
|
|
6515
|
-
context.pathPrefix = relative4(fernRoot, productDir).replace(/\\/g, "/");
|
|
6516
|
-
try {
|
|
6517
|
-
return buildTabsFromConfig(
|
|
6518
|
-
productConfig,
|
|
6519
|
-
productDir,
|
|
6520
|
-
fernRoot,
|
|
6521
|
-
routeSegment ? [routeSegment] : [],
|
|
6522
|
-
label,
|
|
6523
|
-
context
|
|
6524
|
-
);
|
|
6525
|
-
} finally {
|
|
6526
|
-
context.pathPrefix = priorPrefix;
|
|
6502
|
+
if (character === '"' || character === "'" || character === "`") {
|
|
6503
|
+
quote = character;
|
|
6504
|
+
continue;
|
|
6527
6505
|
}
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
function projectFernNavigation(input) {
|
|
6531
|
-
const context = {
|
|
6532
|
-
fernRoot: input.fernRoot,
|
|
6533
|
-
repositoryRoot: input.repositoryRoot ?? input.fernRoot,
|
|
6534
|
-
descriptors: [],
|
|
6535
|
-
seenNavigationIds: /* @__PURE__ */ new Set(),
|
|
6536
|
-
apiSections: [],
|
|
6537
|
-
changelogIndexes: [],
|
|
6538
|
-
warnings: [],
|
|
6539
|
-
warningKeys: /* @__PURE__ */ new Set(),
|
|
6540
|
-
externalLinks: [],
|
|
6541
|
-
bareRouteRedirects: [],
|
|
6542
|
-
segmentAliases: /* @__PURE__ */ new Map(),
|
|
6543
|
-
pathPrefix: "",
|
|
6544
|
-
sourcePathToNavigationId: /* @__PURE__ */ new Map()
|
|
6545
|
-
};
|
|
6546
|
-
const config = input.config;
|
|
6547
|
-
const hasProducts = Array.isArray(config.products) && config.products.length > 0 && !Array.isArray(config.navigation);
|
|
6548
|
-
const tabs = hasProducts ? projectFernProducts(config.products, input.fernRoot, context) : buildTabsFromConfig(config, input.fernRoot, input.fernRoot, [], "Documentation", context);
|
|
6549
|
-
if (tabs.length === 0) {
|
|
6550
|
-
context.warnings.push({
|
|
6551
|
-
code: "unsupported-config",
|
|
6552
|
-
message: "Fern navigation could not be projected; generated navigation will be used."
|
|
6553
|
-
});
|
|
6506
|
+
if (character === "[") depth++;
|
|
6507
|
+
if (character === "]" && --depth === 0) return source.slice(start, index + 1);
|
|
6554
6508
|
}
|
|
6555
|
-
|
|
6556
|
-
|
|
6557
|
-
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6561
|
-
|
|
6562
|
-
|
|
6563
|
-
|
|
6509
|
+
return null;
|
|
6510
|
+
}
|
|
6511
|
+
function configuredSidebarPath(repositoryRoot) {
|
|
6512
|
+
for (const filename of ["docusaurus.config.js", "docusaurus.config.ts", "docusaurus.config.mjs"]) {
|
|
6513
|
+
const path = resolveWithin(repositoryRoot, filename);
|
|
6514
|
+
if (!existsSync4(path)) continue;
|
|
6515
|
+
const source = readBoundedText(path);
|
|
6516
|
+
for (const docs of source.matchAll(/\bdocs\s*:\s*/g)) {
|
|
6517
|
+
const object = matchingObjectLiteral(source, (docs.index ?? 0) + docs[0].length);
|
|
6518
|
+
const candidate2 = object && staticStringField(object, "sidebarPath")?.replace(/^\.\//, "");
|
|
6519
|
+
if (candidate2) {
|
|
6520
|
+
const resolved2 = withinRealRoot(resolveWithin(repositoryRoot, candidate2), repositoryRoot);
|
|
6521
|
+
if (existsSync4(resolved2) && lstatSync4(resolved2).isFile()) return candidate2;
|
|
6522
|
+
}
|
|
6564
6523
|
}
|
|
6524
|
+
const match = source.match(/\bsidebarPath\s*:\s*(?:require\.resolve\(\s*)?(['"])([^'"]+)\1/);
|
|
6525
|
+
if (!match) continue;
|
|
6526
|
+
const candidate = match[2].replace(/^\.\//, "");
|
|
6527
|
+
const resolved = withinRealRoot(resolveWithin(repositoryRoot, candidate), repositoryRoot);
|
|
6528
|
+
if (existsSync4(resolved) && lstatSync4(resolved).isFile()) return candidate;
|
|
6565
6529
|
}
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
const
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
6581
|
-
|
|
6582
|
-
|
|
6530
|
+
return null;
|
|
6531
|
+
}
|
|
6532
|
+
function readDocusaurusSidebars(repositoryRoot, versionedSidebarPath) {
|
|
6533
|
+
const configured = versionedSidebarPath ?? configuredSidebarPath(repositoryRoot);
|
|
6534
|
+
const sourcePath = [configured, ...versionedSidebarPath ? [] : SIDEBAR_FILENAMES].filter((value) => Boolean(value)).find((candidate) => {
|
|
6535
|
+
const path = withinRealRoot(resolveWithin(repositoryRoot, candidate), repositoryRoot);
|
|
6536
|
+
return existsSync4(path) && lstatSync4(path).isFile();
|
|
6537
|
+
});
|
|
6538
|
+
if (!sourcePath) return null;
|
|
6539
|
+
const absolutePath = withinRealRoot(resolveWithin(repositoryRoot, sourcePath), repositoryRoot);
|
|
6540
|
+
const source = readBoundedText(absolutePath);
|
|
6541
|
+
const parsed = extname5(sourcePath).toLowerCase() === ".json" ? objectValue4(JSON5.parse(source)) : parseStaticSidebarModule(source);
|
|
6542
|
+
if (!parsed) throw new Error("Docusaurus sidebar config must export an object.");
|
|
6543
|
+
return { config: parsed, sourcePath };
|
|
6544
|
+
}
|
|
6545
|
+
function namedExportObjectLiteralText(source, name) {
|
|
6546
|
+
const normalizedSource = replaceExternalFbContent(source);
|
|
6547
|
+
const match = new RegExp(`\\bexport\\s+(?:const|let|var)\\s+${escapeRegExp(name)}(?![\\w$])(?:\\s*:\\s*(?:(?!\\b(?:const|let|var)\\b)[^=;])+)?\\s*=\\s*`).exec(normalizedSource);
|
|
6548
|
+
if (!match) return null;
|
|
6549
|
+
return matchingObjectLiteral(normalizedSource, match.index + match[0].length);
|
|
6550
|
+
}
|
|
6551
|
+
function redirectEntriesFromObjectLiteralText(objectLiteral, warnings) {
|
|
6552
|
+
const match = /\bredirects\s*:\s*/.exec(objectLiteral);
|
|
6553
|
+
if (!match) return [];
|
|
6554
|
+
const arrayStart = objectLiteral.indexOf("[", match.index + match[0].length);
|
|
6555
|
+
if (arrayStart < 0) return [];
|
|
6556
|
+
const arrayLiteral = matchingArrayLiteral(objectLiteral, arrayStart);
|
|
6557
|
+
if (!arrayLiteral) return [];
|
|
6558
|
+
let parsed;
|
|
6559
|
+
try {
|
|
6560
|
+
parsed = JSON5.parse(arrayLiteral);
|
|
6561
|
+
} catch {
|
|
6562
|
+
return [];
|
|
6563
|
+
}
|
|
6564
|
+
if (!Array.isArray(parsed)) return [];
|
|
6565
|
+
const entries = [];
|
|
6566
|
+
for (const raw of parsed) {
|
|
6567
|
+
const entry = objectValue4(raw);
|
|
6568
|
+
if (!entry || typeof entry.to !== "string") continue;
|
|
6569
|
+
const froms = Array.isArray(entry.from) ? entry.from : [entry.from];
|
|
6570
|
+
for (const from of froms) {
|
|
6571
|
+
if (typeof from !== "string") continue;
|
|
6572
|
+
if (!isRedirectPathSafe(from, entry.to)) continue;
|
|
6573
|
+
const translated = translateRedirectWildcards(from, entry.to);
|
|
6574
|
+
if (!translated) {
|
|
6575
|
+
warnings.push({ code: "unsupported-config", message: `Redirect from ${from} uses a wildcard Next.js cannot express and was dropped.` });
|
|
6576
|
+
continue;
|
|
6577
|
+
}
|
|
6578
|
+
entries.push(translated);
|
|
6583
6579
|
}
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6580
|
+
}
|
|
6581
|
+
return entries;
|
|
6582
|
+
}
|
|
6583
|
+
function readDocusaurusRedirects(repositoryRoot, warnings) {
|
|
6584
|
+
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
6585
|
+
if (!configPath) return [];
|
|
6586
|
+
const configSource = readBoundedText(configPath);
|
|
6587
|
+
const pluginMatch = /['"]@docusaurus\/plugin-client-redirects['"]\s*,\s*/.exec(configSource);
|
|
6588
|
+
if (!pluginMatch) return [];
|
|
6589
|
+
const afterPlugin = configSource.slice(pluginMatch.index + pluginMatch[0].length);
|
|
6590
|
+
const warnAboutCreateRedirects = (source) => {
|
|
6591
|
+
if (!/\bcreateRedirects\s*[:(]/.test(source)) return;
|
|
6592
|
+
warnings.push({
|
|
6593
|
+
code: "unsupported-config",
|
|
6594
|
+
message: "The redirects plugin's createRedirects function computes redirects dynamically and cannot be evaluated during migration; add its redirects manually."
|
|
6595
|
+
});
|
|
6596
|
+
};
|
|
6597
|
+
warnAboutCreateRedirects(configSource);
|
|
6598
|
+
const inlineLiteral = matchingObjectLiteral(configSource, pluginMatch.index + pluginMatch[0].length);
|
|
6599
|
+
if (inlineLiteral) return redirectEntriesFromObjectLiteralText(inlineLiteral, warnings);
|
|
6600
|
+
const identifier = afterPlugin.match(/^([A-Za-z_$][\w$]*)/)?.[1];
|
|
6601
|
+
if (!identifier) return [];
|
|
6602
|
+
const importMatch = new RegExp(`import\\s*\\{[^}]*(?<![\\w$])${escapeRegExp(identifier)}(?![\\w$])[^}]*\\}\\s*from\\s*(['"])([^'"]+)\\1`).exec(configSource) ?? new RegExp(`import\\s+${escapeRegExp(identifier)}\\s+from\\s*(['"])([^'"]+)\\1`).exec(configSource);
|
|
6603
|
+
const modulePath = importMatch?.[2];
|
|
6604
|
+
if (!modulePath || !modulePath.startsWith(".")) return [];
|
|
6605
|
+
let resolvedPath;
|
|
6606
|
+
try {
|
|
6607
|
+
resolvedPath = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs"].map((extension) => withinRealRoot(resolveWithin(repositoryRoot, `${modulePath.replace(/^\.\//, "")}${extension}`), repositoryRoot)).find((candidate) => existsSync4(candidate) && lstatSync4(candidate).isFile());
|
|
6608
|
+
} catch {
|
|
6609
|
+
return [];
|
|
6610
|
+
}
|
|
6611
|
+
if (!resolvedPath) return [];
|
|
6612
|
+
const moduleSource = readBoundedText(resolvedPath);
|
|
6613
|
+
warnAboutCreateRedirects(moduleSource);
|
|
6614
|
+
const literal = namedExportObjectLiteralText(moduleSource, identifier);
|
|
6615
|
+
return literal ? redirectEntriesFromObjectLiteralText(literal, warnings) : [];
|
|
6616
|
+
}
|
|
6617
|
+
function readCategoryMetadata(contentRoot, directory) {
|
|
6618
|
+
for (const filename of CATEGORY_FILENAMES) {
|
|
6619
|
+
const path = resolveWithin(contentRoot, posix3.join(directory, filename));
|
|
6620
|
+
try {
|
|
6621
|
+
withinRealRoot(path, contentRoot);
|
|
6622
|
+
} catch {
|
|
6623
|
+
continue;
|
|
6624
|
+
}
|
|
6625
|
+
if (!existsSync4(path) || !lstatSync4(path).isFile()) continue;
|
|
6626
|
+
try {
|
|
6627
|
+
const raw = readBoundedText(path);
|
|
6628
|
+
const parsed = filename.endsWith(".json") ? JSON5.parse(raw) : parseYaml3(raw);
|
|
6629
|
+
const object = objectValue4(parsed);
|
|
6630
|
+
if (!object) return {};
|
|
6631
|
+
return {
|
|
6632
|
+
...typeof object.label === "string" ? { label: object.label } : {},
|
|
6633
|
+
...typeof object.position === "number" ? { position: object.position } : {},
|
|
6634
|
+
..."link" in object ? { link: object.link } : {}
|
|
6635
|
+
};
|
|
6636
|
+
} catch {
|
|
6637
|
+
return {};
|
|
6638
|
+
}
|
|
6639
|
+
}
|
|
6640
|
+
return {};
|
|
6641
|
+
}
|
|
6642
|
+
function descriptorSort(left, right) {
|
|
6643
|
+
const leftPosition = left.sidebarPosition ?? Number.MAX_SAFE_INTEGER;
|
|
6644
|
+
const rightPosition = right.sidebarPosition ?? Number.MAX_SAFE_INTEGER;
|
|
6645
|
+
if (leftPosition !== rightPosition) return leftPosition - rightPosition;
|
|
6646
|
+
return left.sourcePath.localeCompare(right.sourcePath, void 0, { numeric: true });
|
|
6647
|
+
}
|
|
6648
|
+
function registerDoc(docId, context, sourceDirectory) {
|
|
6649
|
+
const key = normalizeDocId(docId);
|
|
6650
|
+
const scopedKey = sourceDirectory && !docId.startsWith("/") ? normalizeDocId(posix3.join(sourceDirectory, key)) : void 0;
|
|
6651
|
+
const descriptor = (scopedKey ? context.descriptorByDocId.get(scopedKey) : void 0) ?? context.descriptorByDocId.get(key) ?? context.descriptors.find((page) => page.navigationId === docusaurusRouteId(key));
|
|
6652
|
+
if (!descriptor) {
|
|
6653
|
+
context.warnings.push({
|
|
6654
|
+
code: "missing-page",
|
|
6655
|
+
message: "A Docusaurus sidebar document did not resolve to an imported page.",
|
|
6656
|
+
source: docId
|
|
6657
|
+
});
|
|
6658
|
+
return null;
|
|
6659
|
+
}
|
|
6660
|
+
context.referencedNavigationIds.add(descriptor.navigationId);
|
|
6661
|
+
return descriptor.navigationId;
|
|
6662
|
+
}
|
|
6663
|
+
function generatedIndexPage(label, linkValue, context, sourceDirectory) {
|
|
6664
|
+
const link = objectValue4(linkValue);
|
|
6665
|
+
if (!link) return null;
|
|
6666
|
+
if (link.type === "doc" && typeof link.id === "string") return registerDoc(link.id, context, sourceDirectory);
|
|
6667
|
+
if (link.type !== "generated-index") return null;
|
|
6668
|
+
const fallback = `category/${slugifySegment(label)}`;
|
|
6669
|
+
const unprefixedNavigationId = typeof link.slug === "string" ? routeFromSlug(link.slug, fallback) ?? fallback : fallback;
|
|
6670
|
+
const navigationId = context.routePrefix && unprefixedNavigationId !== context.routePrefix && !unprefixedNavigationId.startsWith(`${context.routePrefix}/`) ? posix3.join(context.routePrefix, unprefixedNavigationId) : unprefixedNavigationId;
|
|
6671
|
+
if (!context.generatedIds.has(navigationId) && !context.descriptors.some((page) => page.navigationId === navigationId)) {
|
|
6672
|
+
const description = typeof link.description === "string" ? link.description : `Browse the ${label} documentation.`;
|
|
6673
|
+
context.generatedPages.push({
|
|
6674
|
+
id: storageIdForRoute(navigationId),
|
|
6675
|
+
navigationId,
|
|
6676
|
+
title: typeof link.title === "string" ? link.title : label,
|
|
6677
|
+
description,
|
|
6678
|
+
keywords: Array.isArray(link.keywords) ? link.keywords.filter((value) => typeof value === "string") : [],
|
|
6679
|
+
body: description,
|
|
6680
|
+
source: `${context.sourceUrl}#${context.sidebarSource}`
|
|
6681
|
+
});
|
|
6682
|
+
context.generatedIds.add(navigationId);
|
|
6683
|
+
}
|
|
6684
|
+
context.referencedNavigationIds.add(navigationId);
|
|
6685
|
+
return navigationId;
|
|
6686
|
+
}
|
|
6687
|
+
var DOCUSAURUS_HEX_COLOR = /^#[0-9a-fA-F]{3,8}$/;
|
|
6688
|
+
function findDocusaurusConfigPath(repositoryRoot) {
|
|
6689
|
+
return ["docusaurus.config.js", "docusaurus.config.ts", "docusaurus.config.mjs"].map((filename) => resolveWithin(repositoryRoot, filename)).find((candidate) => existsSync4(candidate) && lstatSync4(candidate).isFile()) ?? null;
|
|
6690
|
+
}
|
|
6691
|
+
function readDocusaurusThemeColor(repositoryRoot) {
|
|
6692
|
+
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
6693
|
+
if (!configPath) return void 0;
|
|
6694
|
+
const configSource = readBoundedText(configPath);
|
|
6695
|
+
const cssRelativePath = configSource.match(/\bcustomCss\s*:\s*(?:\[\s*)?(?:require\.resolve\(\s*)?(['"])([^'"]+)\1/)?.[2];
|
|
6696
|
+
if (!cssRelativePath) return void 0;
|
|
6697
|
+
let cssPath;
|
|
6698
|
+
try {
|
|
6699
|
+
cssPath = withinRealRoot(resolveWithin(repositoryRoot, cssRelativePath.replace(/^\.\//, "")), repositoryRoot);
|
|
6700
|
+
} catch {
|
|
6701
|
+
return void 0;
|
|
6702
|
+
}
|
|
6703
|
+
if (!existsSync4(cssPath) || !lstatSync4(cssPath).isFile()) return void 0;
|
|
6704
|
+
const css = readBoundedText(cssPath);
|
|
6705
|
+
const pick = (block) => {
|
|
6706
|
+
const value = block?.match(/--ifm-color-primary\s*:\s*([^;]+);/)?.[1]?.trim();
|
|
6707
|
+
return value && DOCUSAURUS_HEX_COLOR.test(value) ? value : void 0;
|
|
6708
|
+
};
|
|
6709
|
+
const lightMode = pick(css.match(/:root\s*\{([^}]*)\}/)?.[1]);
|
|
6710
|
+
const darkMode = pick(css.match(/\[data-theme=(['"])dark\1\]\s*\{([^}]*)\}/)?.[2]);
|
|
6711
|
+
const colors = {};
|
|
6712
|
+
if (darkMode) colors.light = darkMode;
|
|
6713
|
+
if (lightMode) colors.dark = lightMode;
|
|
6714
|
+
return Object.keys(colors).length > 0 ? colors : void 0;
|
|
6715
|
+
}
|
|
6716
|
+
function propertyStart(source, name) {
|
|
6717
|
+
const pattern = new RegExp(`["']?${name}["']?\\s*:\\s*`, "g");
|
|
6718
|
+
for (let match = pattern.exec(source); match; match = pattern.exec(source)) {
|
|
6719
|
+
let before = match.index - 1;
|
|
6720
|
+
while (before >= 0 && (source[before] === " " || source[before] === " ")) before--;
|
|
6721
|
+
if (before < 0 || "\r\n,{".includes(source[before])) return match.index + match[0].length;
|
|
6722
|
+
}
|
|
6723
|
+
return -1;
|
|
6724
|
+
}
|
|
6725
|
+
function staticStringBindings(source) {
|
|
6726
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
6727
|
+
for (const match of source.matchAll(/\bconst\s+([A-Z][A-Z0-9_]*)\s*=\s*((?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'))/g)) {
|
|
6728
|
+
try {
|
|
6729
|
+
const value = JSON5.parse(match[2]);
|
|
6730
|
+
if (typeof value === "string") bindings.set(match[1], value);
|
|
6731
|
+
} catch {
|
|
6732
|
+
}
|
|
6733
|
+
}
|
|
6734
|
+
return bindings;
|
|
6735
|
+
}
|
|
6736
|
+
function staticArrayField(source, name) {
|
|
6737
|
+
const start = propertyStart(source, name);
|
|
6738
|
+
return start < 0 ? null : matchingArrayLiteral(source, start);
|
|
6739
|
+
}
|
|
6740
|
+
function staticObjectEntries(array) {
|
|
6741
|
+
if (!array) return [];
|
|
6742
|
+
const entries = [];
|
|
6743
|
+
let afterDelimiter = 1;
|
|
6744
|
+
let quote = "";
|
|
6745
|
+
let lineComment = false;
|
|
6746
|
+
let blockComment = false;
|
|
6747
|
+
for (let index = 1; index < array.length - 1; index++) {
|
|
6748
|
+
const char = array[index];
|
|
6749
|
+
const next = array[index + 1];
|
|
6750
|
+
if (lineComment) {
|
|
6751
|
+
if (char === "\n") lineComment = false;
|
|
6752
|
+
continue;
|
|
6753
|
+
}
|
|
6754
|
+
if (blockComment) {
|
|
6755
|
+
if (char === "*" && next === "/") {
|
|
6756
|
+
blockComment = false;
|
|
6757
|
+
index++;
|
|
6758
|
+
}
|
|
6759
|
+
continue;
|
|
6760
|
+
}
|
|
6761
|
+
if (quote) {
|
|
6762
|
+
if (char === "\\") {
|
|
6763
|
+
index++;
|
|
6764
|
+
continue;
|
|
6765
|
+
}
|
|
6766
|
+
if (char === quote) quote = "";
|
|
6767
|
+
continue;
|
|
6768
|
+
}
|
|
6769
|
+
if (char === "/" && next === "/") {
|
|
6770
|
+
lineComment = true;
|
|
6771
|
+
index++;
|
|
6772
|
+
continue;
|
|
6773
|
+
}
|
|
6774
|
+
if (char === "/" && next === "*") {
|
|
6775
|
+
blockComment = true;
|
|
6776
|
+
index++;
|
|
6777
|
+
continue;
|
|
6778
|
+
}
|
|
6779
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
6780
|
+
quote = char;
|
|
6781
|
+
continue;
|
|
6782
|
+
}
|
|
6783
|
+
if (char === ",") {
|
|
6784
|
+
afterDelimiter = index + 1;
|
|
6785
|
+
continue;
|
|
6786
|
+
}
|
|
6787
|
+
if (char !== "{") continue;
|
|
6788
|
+
const literal = matchingObjectLiteral(array, index);
|
|
6789
|
+
if (!literal) continue;
|
|
6790
|
+
const lead = array.slice(afterDelimiter, index).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, "").trim();
|
|
6791
|
+
if (!lead) entries.push(literal);
|
|
6792
|
+
index += literal.length - 1;
|
|
6793
|
+
}
|
|
6794
|
+
return entries;
|
|
6795
|
+
}
|
|
6796
|
+
function staticStringField(source, name, bindings) {
|
|
6797
|
+
const start = propertyStart(source, name);
|
|
6798
|
+
if (start < 0) return void 0;
|
|
6799
|
+
const quoted = source.slice(start).match(/^("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')/);
|
|
6800
|
+
if (!quoted) {
|
|
6801
|
+
const binding = source.slice(start).match(/^([A-Z][A-Z0-9_]*)\b/)?.[1];
|
|
6802
|
+
return binding ? bindings?.get(binding) : void 0;
|
|
6803
|
+
}
|
|
6804
|
+
try {
|
|
6805
|
+
const parsed = JSON5.parse(quoted[1]);
|
|
6806
|
+
return typeof parsed === "string" ? parsed : void 0;
|
|
6807
|
+
} catch {
|
|
6808
|
+
return void 0;
|
|
6809
|
+
}
|
|
6810
|
+
}
|
|
6811
|
+
function readDocusaurusSiteSettings(repositoryRoot) {
|
|
6812
|
+
const empty2 = { docsRouteBasePath: "docs", defaultColorMode: "light", navbarLinks: [], footerLinks: [] };
|
|
6813
|
+
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
6814
|
+
if (!configPath) return empty2;
|
|
6815
|
+
const source = readBoundedText(configPath);
|
|
6816
|
+
const bindings = staticStringBindings(source);
|
|
6817
|
+
const presets = staticArrayField(source, "presets");
|
|
6818
|
+
const classicDocs = presets && [...presets.matchAll(/["']?docs["']?\s*:\s*/g)].map((match) => matchingObjectLiteral(presets, (match.index ?? 0) + match[0].length)).find((object) => Boolean(object));
|
|
6819
|
+
const docsRouteBasePath = classicDocs && staticStringField(classicDocs, "routeBasePath") || "docs";
|
|
6820
|
+
const themeStart = propertyStart(source, "themeConfig");
|
|
6821
|
+
const theme = themeStart < 0 ? null : matchingObjectLiteral(source, themeStart);
|
|
6822
|
+
const colorModeStart = theme ? propertyStart(theme, "colorMode") : -1;
|
|
6823
|
+
const colorMode = colorModeStart < 0 ? null : matchingObjectLiteral(theme, colorModeStart);
|
|
6824
|
+
const configuredColorMode = colorMode ? staticStringField(colorMode, "defaultMode") : void 0;
|
|
6825
|
+
const defaultColorMode = configuredColorMode === "dark" ? "dark" : "light";
|
|
6826
|
+
const navbarStart = theme ? propertyStart(theme, "navbar") : -1;
|
|
6827
|
+
const navbar = navbarStart < 0 ? null : matchingObjectLiteral(theme, navbarStart);
|
|
6828
|
+
const logoStart = navbar ? propertyStart(navbar, "logo") : -1;
|
|
6829
|
+
const logo = logoStart < 0 ? null : matchingObjectLiteral(navbar, logoStart);
|
|
6830
|
+
const footerStart = theme ? propertyStart(theme, "footer") : -1;
|
|
6831
|
+
const footer = footerStart < 0 ? null : matchingObjectLiteral(theme, footerStart);
|
|
6832
|
+
const navbarLinks = navbar ? staticObjectEntries(staticArrayField(navbar, "items")).flatMap((item) => {
|
|
6833
|
+
if (propertyStart(item, "items") >= 0 || propertyStart(item, "dropdownItemsAfter") >= 0) return [];
|
|
6834
|
+
const docsPluginId = staticStringField(item, "docsPluginId", bindings);
|
|
6835
|
+
const href = staticStringField(item, "to", bindings) ?? staticStringField(item, "href", bindings);
|
|
6836
|
+
const docId = staticStringField(item, "docId", bindings);
|
|
6837
|
+
const sidebarId = staticStringField(item, "sidebarId", bindings);
|
|
6838
|
+
const label = staticStringField(item, "label", bindings) ?? staticStringField(item, "aria-label", bindings);
|
|
6839
|
+
return label && (href || docId || sidebarId || docsPluginId) ? [{
|
|
6840
|
+
label,
|
|
6841
|
+
...href ? { href } : {},
|
|
6842
|
+
...docId ? { docId } : {},
|
|
6843
|
+
...sidebarId ? { sidebarId } : {},
|
|
6844
|
+
...docsPluginId ? { docsPluginId } : {}
|
|
6845
|
+
}] : [];
|
|
6589
6846
|
}) : [];
|
|
6590
|
-
const
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
const literal = context.segmentAliases.get(segment);
|
|
6598
|
-
if (!literal) return segment;
|
|
6599
|
-
changed = true;
|
|
6600
|
-
return literal;
|
|
6847
|
+
const footerLinks = footer ? staticObjectEntries(staticArrayField(footer, "links")).flatMap((column) => {
|
|
6848
|
+
const heading = staticStringField(column, "title", bindings);
|
|
6849
|
+
if (!heading) return [];
|
|
6850
|
+
const items = staticObjectEntries(staticArrayField(column, "items")).flatMap((item) => {
|
|
6851
|
+
const href = staticStringField(item, "to", bindings) ?? staticStringField(item, "href", bindings);
|
|
6852
|
+
const label = staticStringField(item, "label", bindings);
|
|
6853
|
+
return label && href ? [{ label, href }] : [];
|
|
6601
6854
|
});
|
|
6602
|
-
|
|
6603
|
-
const source = `/${aliased.join("/")}`;
|
|
6604
|
-
const destination = `/${keptNavigationId(descriptor)}`;
|
|
6605
|
-
if (source === destination || knownIds.has(aliased.join("/"))) return [];
|
|
6606
|
-
return [{ source, destination }];
|
|
6855
|
+
return items.length > 0 ? [{ heading, items }] : [];
|
|
6607
6856
|
}) : [];
|
|
6608
|
-
const
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6857
|
+
const copyright = footer && propertyStart(footer, "copyright") >= 0 ? staticStringField(footer, "copyright") ?? footer.slice(propertyStart(footer, "copyright")).match(/^`([^`]+)`/)?.[1]?.replace(/\$\{new Date\(\)\.getFullYear\(\)\}/g, "{year}") : void 0;
|
|
6858
|
+
const announcementStart = theme ? propertyStart(theme, "announcementBar") : -1;
|
|
6859
|
+
const announcement = announcementStart < 0 ? null : matchingObjectLiteral(theme, announcementStart);
|
|
6860
|
+
const announcementContent = announcement && staticStringField(announcement, "content") || announcement?.slice(propertyStart(announcement, "content")).match(/^`([^`]+)`/)?.[1];
|
|
6861
|
+
const bannerContent = announcementContent ? announcementMarkdown(announcementContent) : "";
|
|
6862
|
+
return {
|
|
6863
|
+
...bannerContent ? { banner: { content: bannerContent, dismissible: !/isCloseable\s*:\s*false/.test(announcement), ...staticStringField(announcement, "id") ? { id: staticStringField(announcement, "id") } : {} } } : {},
|
|
6864
|
+
name: staticStringField(source, "title"),
|
|
6865
|
+
description: staticStringField(source, "tagline"),
|
|
6866
|
+
...navbar && staticStringField(navbar, "title") ? { navbarTitle: staticStringField(navbar, "title") } : {},
|
|
6867
|
+
defaultColorMode,
|
|
6868
|
+
docsRouteBasePath,
|
|
6869
|
+
...logo && staticStringField(logo, "src") ? {
|
|
6870
|
+
logo: {
|
|
6871
|
+
light: staticStringField(logo, "src"),
|
|
6872
|
+
...staticStringField(logo, "srcDark") ? { dark: staticStringField(logo, "srcDark") } : {},
|
|
6873
|
+
showTitle: Boolean(navbar && staticStringField(navbar, "title"))
|
|
6874
|
+
}
|
|
6875
|
+
} : {},
|
|
6876
|
+
...staticStringField(source, "favicon") ? { favicon: staticStringField(source, "favicon") } : {},
|
|
6877
|
+
navbarLinks,
|
|
6878
|
+
footerLinks,
|
|
6879
|
+
...copyright ? { copyright } : {}
|
|
6880
|
+
};
|
|
6881
|
+
}
|
|
6882
|
+
function autogeneratedItems(dirName, context) {
|
|
6883
|
+
const normalizedDir = dirName === "." ? "" : dirName.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
|
|
6884
|
+
const inDirectory = context.descriptors.filter((descriptor) => {
|
|
6885
|
+
const source = descriptor.sourcePath.replace(/\.(?:mdx?)$/i, "");
|
|
6886
|
+
return !normalizedDir || source === normalizedDir || source.startsWith(`${normalizedDir}/`);
|
|
6887
|
+
});
|
|
6888
|
+
const directPages = [];
|
|
6889
|
+
const childDirectories = /* @__PURE__ */ new Set();
|
|
6890
|
+
for (const descriptor of inDirectory) {
|
|
6891
|
+
const source = descriptor.sourcePath.replace(/\.(?:mdx?)$/i, "");
|
|
6892
|
+
const relativePath = normalizedDir ? posix3.relative(normalizedDir, source) : source;
|
|
6893
|
+
const segments = relativePath.split("/").filter(Boolean);
|
|
6894
|
+
if (segments.length <= 1) directPages.push(descriptor);
|
|
6895
|
+
else childDirectories.add(segments[0]);
|
|
6896
|
+
}
|
|
6897
|
+
const pages = directPages.map((descriptor) => ({
|
|
6898
|
+
// An index document is the category landing page in Docusaurus. Thally
|
|
6899
|
+
// renders it as a child link, so put it before the category's other docs.
|
|
6900
|
+
position: descriptor.sidebarPosition ?? (/(?:^|\/)(?:index|readme)\.mdx?$/i.test(descriptor.sourcePath) ? -1 : Number.MAX_SAFE_INTEGER),
|
|
6901
|
+
// Siblings sort by file or directory name, like the category entries below.
|
|
6902
|
+
sortKey: posix3.basename(descriptor.sourcePath),
|
|
6903
|
+
item: descriptor.navigationId
|
|
6904
|
+
}));
|
|
6905
|
+
const groups = [...childDirectories].map((segment) => {
|
|
6906
|
+
const directory = normalizedDir ? posix3.join(normalizedDir, segment) : segment;
|
|
6907
|
+
const metadata = readCategoryMetadata(context.contentRoot, directory);
|
|
6908
|
+
const pages2 = autogeneratedItems(directory, context);
|
|
6909
|
+
if (pages2.length === 1 && typeof pages2[0] === "string" && !metadata.label && metadata.position === void 0 && !metadata.link) {
|
|
6910
|
+
const onlyPage = context.descriptors.find((descriptor) => descriptor.navigationId === pages2[0]);
|
|
6911
|
+
if (onlyPage && /(?:^|\/)(?:index|readme)\.mdx?$/i.test(onlyPage.sourcePath)) {
|
|
6912
|
+
return {
|
|
6913
|
+
position: onlyPage.sidebarPosition ?? Number.MAX_SAFE_INTEGER,
|
|
6914
|
+
sortKey: segment,
|
|
6915
|
+
item: pages2[0]
|
|
6916
|
+
};
|
|
6917
|
+
}
|
|
6918
|
+
}
|
|
6919
|
+
const landing = generatedIndexPage(metadata.label ?? titleCase3(segment), metadata.link, context, directory);
|
|
6920
|
+
if (landing && !pages2.includes(landing)) pages2.unshift(landing);
|
|
6921
|
+
return {
|
|
6922
|
+
position: metadata.position ?? Number.MAX_SAFE_INTEGER,
|
|
6923
|
+
sortKey: segment,
|
|
6924
|
+
item: {
|
|
6925
|
+
group: metadata.label ?? titleCase3(segment),
|
|
6926
|
+
pages: pages2
|
|
6927
|
+
}
|
|
6928
|
+
};
|
|
6929
|
+
});
|
|
6930
|
+
return [...pages, ...groups].sort((left, right) => left.position - right.position || left.sortKey.localeCompare(right.sortKey, void 0, { numeric: true })).map(({ item }) => {
|
|
6931
|
+
if (typeof item === "string") context.referencedNavigationIds.add(item);
|
|
6932
|
+
return item;
|
|
6933
|
+
});
|
|
6934
|
+
}
|
|
6935
|
+
function convertItems(value, context) {
|
|
6936
|
+
if (typeof value === "string") {
|
|
6937
|
+
const page = registerDoc(value, context);
|
|
6938
|
+
return page ? [page] : [];
|
|
6939
|
+
}
|
|
6940
|
+
if (Array.isArray(value)) return value.flatMap((entry) => convertItems(entry, context));
|
|
6941
|
+
const object = objectValue4(value);
|
|
6942
|
+
if (!object) return [];
|
|
6943
|
+
if (object.type === "doc" || object.type === "ref") {
|
|
6944
|
+
const page = typeof object.id === "string" ? registerDoc(object.id, context) : null;
|
|
6945
|
+
return page ? [page] : [];
|
|
6946
|
+
}
|
|
6947
|
+
if (object.type === "autogenerated") {
|
|
6948
|
+
return autogeneratedItems(typeof object.dirName === "string" ? object.dirName : ".", context);
|
|
6949
|
+
}
|
|
6950
|
+
if (object.type === "category") {
|
|
6951
|
+
const label = typeof object.label === "string" ? object.label : "Documentation";
|
|
6952
|
+
const pages = convertItems(object.items, context);
|
|
6953
|
+
const landing = generatedIndexPage(label, object.link, context);
|
|
6954
|
+
if (landing && !pages.includes(landing)) pages.unshift(landing);
|
|
6955
|
+
return pages.length > 0 ? [{ group: label, pages }] : [];
|
|
6956
|
+
}
|
|
6957
|
+
if (object.type === "link" || object.type === "html") return [];
|
|
6958
|
+
return Object.entries(object).flatMap(([label, items]) => {
|
|
6959
|
+
const pages = convertItems(items, context);
|
|
6960
|
+
return pages.length > 0 ? [{ group: label, pages }] : [];
|
|
6961
|
+
});
|
|
6962
|
+
}
|
|
6963
|
+
function projectDocusaurusNavigation(input) {
|
|
6964
|
+
const warnings = [];
|
|
6965
|
+
const descriptorByDocId = new Map(input.descriptors.map((page) => [page.docId, page]));
|
|
6966
|
+
const context = {
|
|
6967
|
+
contentRoot: input.contentRoot,
|
|
6968
|
+
descriptors: input.descriptors,
|
|
6969
|
+
descriptorByDocId,
|
|
6970
|
+
generatedPages: [],
|
|
6971
|
+
generatedIds: /* @__PURE__ */ new Set(),
|
|
6972
|
+
referencedNavigationIds: /* @__PURE__ */ new Set(),
|
|
6973
|
+
sourceUrl: input.sourceUrl,
|
|
6974
|
+
routePrefix: input.routePrefix ?? "",
|
|
6975
|
+
sidebarSource: input.sidebars?.sourcePath ?? "autogenerated sidebar",
|
|
6976
|
+
warnings
|
|
6977
|
+
};
|
|
6978
|
+
const sidebarEntries = input.sidebars ? Object.entries(input.sidebars.config) : [["docs", [
|
|
6979
|
+
{ type: "autogenerated", dirName: "." }
|
|
6980
|
+
]]];
|
|
6981
|
+
const tabs = sidebarEntries.flatMap(([sidebarId, value]) => {
|
|
6982
|
+
const converted = convertItems(value, context);
|
|
6983
|
+
if (converted.length === 0) return [];
|
|
6984
|
+
return [{
|
|
6985
|
+
tab: sidebarEntries.length === 1 ? "Documentation" : titleCase3(sidebarId),
|
|
6986
|
+
// Docusaurus categories are disclosures, including at the sidebar
|
|
6987
|
+
// root. Keeping them as Thally root nodes preserves their collapsed
|
|
6988
|
+
// state and the authored interleaving of documents and categories.
|
|
6989
|
+
pages: converted
|
|
6990
|
+
}];
|
|
6612
6991
|
});
|
|
6613
|
-
const
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
];
|
|
6619
|
-
const claimedTabLabels = /* @__PURE__ */ new Set();
|
|
6620
|
-
const disambiguatedApiSections = context.apiSections.map((section) => {
|
|
6621
|
-
if (!section.tabLabel || !claimedTabLabels.has(section.tabLabel)) {
|
|
6622
|
-
if (section.tabLabel) claimedTabLabels.add(section.tabLabel);
|
|
6623
|
-
return section;
|
|
6624
|
-
}
|
|
6625
|
-
const distinguishingSegment = section.routeSegments.at(-1);
|
|
6626
|
-
let tabLabel = distinguishingSegment ? `${section.tabLabel}: ${titleCase3(distinguishingSegment)}` : section.tabLabel;
|
|
6627
|
-
if (!tabLabel || tabLabel === `${section.tabLabel}: ${section.tabLabel}` || claimedTabLabels.has(tabLabel)) {
|
|
6628
|
-
let ordinal = 2;
|
|
6629
|
-
let candidate = `${section.tabLabel} (${ordinal})`;
|
|
6630
|
-
while (claimedTabLabels.has(candidate)) {
|
|
6631
|
-
ordinal += 1;
|
|
6632
|
-
candidate = `${section.tabLabel} (${ordinal})`;
|
|
6633
|
-
}
|
|
6634
|
-
tabLabel = candidate;
|
|
6635
|
-
}
|
|
6636
|
-
claimedTabLabels.add(tabLabel);
|
|
6637
|
-
return { ...section, tabLabel };
|
|
6992
|
+
const unlisted = new Set(input.descriptors.filter((page) => page.unlisted).map((page) => page.navigationId));
|
|
6993
|
+
const withoutUnlisted = (items) => items.flatMap((item) => {
|
|
6994
|
+
if (typeof item === "string") return unlisted.has(item) ? [] : [item];
|
|
6995
|
+
const pages = withoutUnlisted(item.pages);
|
|
6996
|
+
return pages.length > 0 ? [{ ...item, pages }] : [];
|
|
6638
6997
|
});
|
|
6998
|
+
if (unlisted.size > 0) {
|
|
6999
|
+
for (const tab of tabs) tab.pages = withoutUnlisted(tab.pages);
|
|
7000
|
+
}
|
|
7001
|
+
const unreferenced = input.descriptors.filter((page) => !unlisted.has(page.navigationId) && !context.referencedNavigationIds.has(page.navigationId)).sort(descriptorSort).map((page) => page.navigationId);
|
|
7002
|
+
if (unreferenced.length > 0) {
|
|
7003
|
+
if (tabs.length === 0) tabs.push({ tab: "Documentation", pages: [] });
|
|
7004
|
+
tabs[0].pages?.push({ group: "Additional", pages: unreferenced });
|
|
7005
|
+
}
|
|
6639
7006
|
return {
|
|
6640
|
-
docsConfig: {
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
7007
|
+
docsConfig: { tabs },
|
|
7008
|
+
generatedPages: context.generatedPages,
|
|
7009
|
+
referencedNavigationIds: context.referencedNavigationIds,
|
|
7010
|
+
warnings
|
|
7011
|
+
};
|
|
7012
|
+
}
|
|
7013
|
+
function readDocusaurusPresentation(repositoryRoot) {
|
|
7014
|
+
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
7015
|
+
if (!configPath) return void 0;
|
|
7016
|
+
const config = readBoundedText(configPath);
|
|
7017
|
+
const relative7 = config.match(/\bcustomCss\s*:\s*(?:\[\s*)?(?:require\.resolve\(\s*)?(['"])([^'"]+)\1/)?.[2];
|
|
7018
|
+
if (!relative7) return void 0;
|
|
7019
|
+
let css;
|
|
7020
|
+
try {
|
|
7021
|
+
const path = withinRealRoot(resolveWithin(repositoryRoot, relative7), repositoryRoot);
|
|
7022
|
+
if (!lstatSync4(path).isFile()) return void 0;
|
|
7023
|
+
css = readBoundedText(path);
|
|
7024
|
+
if (/\btheme\(/.test(css) || /\.s[ac]ss$/.test(relative7)) {
|
|
7025
|
+
const candidateDirectory = resolveWithin(repositoryRoot, "build/assets/css");
|
|
7026
|
+
if (existsSync4(candidateDirectory)) {
|
|
7027
|
+
const directory = withinRealRoot(candidateDirectory, repositoryRoot);
|
|
7028
|
+
const compiled = readdirSync2(directory).filter((name) => /^styles[.\w-]*\.css$/.test(name)).sort().slice(0, 8);
|
|
7029
|
+
let totalBytes = 0;
|
|
7030
|
+
css = compiled.map((name) => {
|
|
7031
|
+
const path2 = withinRealRoot(resolveWithin(directory, name), repositoryRoot);
|
|
7032
|
+
const stat = lstatSync4(path2);
|
|
7033
|
+
if (!stat.isFile()) return "";
|
|
7034
|
+
totalBytes += stat.size;
|
|
7035
|
+
if (totalBytes > MAX_CONFIG_BYTES2) throw new Error("Compiled theme exceeds the config size limit.");
|
|
7036
|
+
return readBoundedText(path2);
|
|
7037
|
+
}).join("\n") || css;
|
|
7038
|
+
}
|
|
7039
|
+
}
|
|
7040
|
+
} catch {
|
|
7041
|
+
return void 0;
|
|
7042
|
+
}
|
|
7043
|
+
const modes = { light: /* @__PURE__ */ new Map(), dark: /* @__PURE__ */ new Map() };
|
|
7044
|
+
const banners = { light: /* @__PURE__ */ new Map(), dark: /* @__PURE__ */ new Map() };
|
|
7045
|
+
try {
|
|
7046
|
+
postcss3.parse(css).walkRules((rule) => {
|
|
7047
|
+
if (rule.parent?.type !== "root") return;
|
|
7048
|
+
for (const selector of rule.selector.split(",")) {
|
|
7049
|
+
const key = selector.trim();
|
|
7050
|
+
const target = key === ":root" || key === "html" || /^(?:html)?\[data-theme=['"]?light['"]?\]$/.test(key) ? modes.light : /^(?:html)?\[data-theme=['"]?dark['"]?\]$/.test(key) || key === ".dark" ? modes.dark : void 0;
|
|
7051
|
+
if (target) rule.walkDecls((decl) => {
|
|
7052
|
+
if (decl.prop.startsWith("--ifm-")) target.set(decl.prop, decl.value);
|
|
7053
|
+
});
|
|
7054
|
+
const banner = key === ".theme-announcement-bar" ? banners.light : /^(?:html)?\[data-theme=['"]?dark['"]?\] \.theme-announcement-bar$/.test(key) ? banners.dark : void 0;
|
|
7055
|
+
if (banner) rule.walkDecls((decl) => {
|
|
7056
|
+
banner.set(decl.prop, decl.value);
|
|
7057
|
+
});
|
|
7058
|
+
}
|
|
7059
|
+
});
|
|
7060
|
+
} catch {
|
|
7061
|
+
return void 0;
|
|
7062
|
+
}
|
|
7063
|
+
const value = (name, mode, active = /* @__PURE__ */ new Set()) => {
|
|
7064
|
+
if (active.has(name) || active.size >= 16) return void 0;
|
|
7065
|
+
active.add(name);
|
|
7066
|
+
const raw = modes[mode].get(name) ?? modes.light.get(name);
|
|
7067
|
+
const reference = raw && /^var\((--[\w-]+)\)$/.exec(raw.trim())?.[1];
|
|
7068
|
+
return reference ? value(reference, mode, active) : raw;
|
|
7069
|
+
};
|
|
7070
|
+
const color = (name, mode) => {
|
|
7071
|
+
const raw = value(name, mode);
|
|
7072
|
+
return raw ? cssColorToHex(raw) : void 0;
|
|
7073
|
+
};
|
|
7074
|
+
const mapped = { "--docs-bg": "--ifm-background-color", "--docs-ink": "--ifm-font-color-base", "--docs-ink-2": "--ifm-font-color-base", "--docs-accent": "--ifm-link-color" };
|
|
7075
|
+
const declarations2 = (mode) => Object.entries(mapped).flatMap(([target, source]) => {
|
|
7076
|
+
const hex = color(source, mode);
|
|
7077
|
+
return hex ? [`${target}:${hex}`] : [];
|
|
7078
|
+
}).join(";");
|
|
7079
|
+
const heading = value("--ifm-heading-font-family", "light")?.trim();
|
|
7080
|
+
const headingFont = heading && /^[\w -]{1,80}$/.test(heading) ? heading : void 0;
|
|
7081
|
+
const dimension = (name, fallback) => {
|
|
7082
|
+
const raw = value(name, "light");
|
|
7083
|
+
return raw && /^\d+(?:\.\d+)?(?:px|rem)$/.test(raw) ? raw : fallback;
|
|
6650
7084
|
};
|
|
7085
|
+
const bodyFont = value("--ifm-font-family-base", "light");
|
|
7086
|
+
const safeBodyFont = bodyFont && /^[\w\s,'"._-]{1,250}$/.test(bodyFont) ? bodyFont : "ui-sans-serif,system-ui,sans-serif";
|
|
7087
|
+
const bannerCss = (mode) => {
|
|
7088
|
+
const styles = [];
|
|
7089
|
+
for (const [property, raw] of banners[mode]) {
|
|
7090
|
+
const reference = /^var\((--[\w-]+)\)$/.exec(raw)?.[1];
|
|
7091
|
+
const resolved = reference ? value(reference, mode) : raw;
|
|
7092
|
+
if (!resolved) continue;
|
|
7093
|
+
const hex = ["background-color", "color"].includes(property) ? cssColorToHex(resolved) : void 0;
|
|
7094
|
+
if (hex) styles.push(`${property}:${hex}`);
|
|
7095
|
+
else if (property === "font-family" && /^[\w\s,'"._-]{1,250}$/.test(resolved) || ["font-size", "padding"].includes(property) && /^(?:\d*\.?\d+(?:px|rem)?\s*){1,4}$/.test(resolved) || property === "font-weight" && /^(?:[1-9]00|bold|normal)$/.test(resolved)) styles.push(`${property}:${resolved}`);
|
|
7096
|
+
}
|
|
7097
|
+
return styles.join(";");
|
|
7098
|
+
};
|
|
7099
|
+
let tokens = "";
|
|
7100
|
+
const prismStart = propertyStart(config, "prism");
|
|
7101
|
+
const prism = prismStart < 0 ? null : matchingObjectLiteral(config, prismStart);
|
|
7102
|
+
const themeStart = prism ? propertyStart(prism, "theme") : -1;
|
|
7103
|
+
const themeExpression = themeStart < 0 ? "" : prism.slice(themeStart);
|
|
7104
|
+
const identifier = /^([\w$]+)/.exec(themeExpression)?.[1];
|
|
7105
|
+
const binding = identifier && config.match(new RegExp(`(?:const|let|var)\\s+${identifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*=\\s*(require\\([^\\n]+)`))?.[1];
|
|
7106
|
+
const themePath = (themeExpression || "").match(/^require\(\s*['"]([^'"]+)['"]/)?.[1] ?? binding?.match(/^require\(\s*['"]([^'"]+)['"]/)?.[1];
|
|
7107
|
+
if (themePath) try {
|
|
7108
|
+
const base = resolveWithin(repositoryRoot, themePath);
|
|
7109
|
+
const path = [base, `${base}.js`, `${base}/index.js`, `${base}/index.cjs.js`].find((candidate) => existsSync4(candidate) && lstatSync4(candidate).isFile());
|
|
7110
|
+
if (path) {
|
|
7111
|
+
const source = readBoundedText(withinRealRoot(path, repositoryRoot));
|
|
7112
|
+
const start = source.search(/(?:module\.exports|(?:const|let|var)\s+\w+)\s*=/);
|
|
7113
|
+
const literal = start < 0 ? null : matchingObjectLiteral(source, source.indexOf("=", start) + 1);
|
|
7114
|
+
const theme = literal ? JSON5.parse(literal) : null;
|
|
7115
|
+
const tokenNames = { keyword: "keyword", string: "string", comment: "comment", function: "function", property: "property", constant: "constant", punctuation: "punctuation", parameter: "parameter" };
|
|
7116
|
+
const colors = /* @__PURE__ */ new Map();
|
|
7117
|
+
for (const [target, raw] of [["--shiki-color-text", theme?.plain?.color], ["--docs-code-bg", theme?.plain?.backgroundColor]]) {
|
|
7118
|
+
const hex = typeof raw === "string" ? cssColorToHex(raw) : void 0;
|
|
7119
|
+
if (hex && target) colors.set(target, hex);
|
|
7120
|
+
}
|
|
7121
|
+
for (const style of theme?.styles ?? []) for (const type of Array.isArray(style.types) ? style.types : []) {
|
|
7122
|
+
const hex = typeof style.style?.color === "string" ? cssColorToHex(style.style.color) : void 0;
|
|
7123
|
+
if (tokenNames[type] && hex) colors.set(`--shiki-token-${tokenNames[type]}`, hex);
|
|
7124
|
+
}
|
|
7125
|
+
tokens = [...colors].map(([name, hex]) => `${name}:${hex}`).join(";");
|
|
7126
|
+
}
|
|
7127
|
+
} catch {
|
|
7128
|
+
}
|
|
7129
|
+
const lightAccent = color("--ifm-color-primary", "light");
|
|
7130
|
+
const darkAccent = color("--ifm-color-primary", "dark");
|
|
7131
|
+
return { headingFont, colors: lightAccent || darkAccent ? { dark: lightAccent, light: darkAccent } : void 0, unresolved: /\btheme\(/.test(css), css: `
|
|
7132
|
+
:root{${declarations2("light")}}
|
|
7133
|
+
:root.dark{${declarations2("dark")}}
|
|
7134
|
+
body{font-family:${safeBodyFont}}
|
|
7135
|
+
.thally-docs-root,.thally-docs-sidebar{background:var(--docs-bg);color:var(--docs-ink)}
|
|
7136
|
+
.thally-docs-topbar{background:var(--docs-bg);backdrop-filter:none}
|
|
7137
|
+
.thally-docs-topbar-inner{height:${dimension("--ifm-navbar-height", "60px")};max-width:none;padding-inline:24px}
|
|
7138
|
+
.thally-docs-collection-row{display:none}
|
|
7139
|
+
.thally-docs-actions{flex:1}.thally-docs-navlinks{order:-1;margin-left:auto;margin-right:12px}
|
|
7140
|
+
.thally-docs-topbar-link svg{display:none}
|
|
7141
|
+
.thally-docs-search,.thally-docs-search>button:first-of-type{width:180px}
|
|
7142
|
+
.thally-docs-search>button:first-of-type{border:1px solid var(--docs-line);border-radius:24px;background:var(--docs-bg)}
|
|
7143
|
+
.thally-ink-banner{${bannerCss("light")}}
|
|
7144
|
+
.dark .thally-ink-banner{${bannerCss("dark")}}
|
|
7145
|
+
.thally-ink-banner-content{color:inherit;font-size:inherit;font-weight:inherit}
|
|
7146
|
+
.thally-ink-banner-content a{color:var(--docs-accent)}
|
|
7147
|
+
.thally-ink-banner-dot{display:none}
|
|
7148
|
+
.thally-docs-eyebrow{display:none}
|
|
7149
|
+
.thally-docs-header h1{font-size:${dimension("--ifm-h1-font-size", "2.5rem")};line-height:1.25;color:var(--docs-ink)}
|
|
7150
|
+
.thally-docs-prose{line-height:1.5;color:var(--docs-ink)}
|
|
7151
|
+
.thally-docs-prose p{margin-block:0 1rem}
|
|
7152
|
+
.thally-docs-prose a{color:var(--docs-accent)}
|
|
7153
|
+
.thally-docs-code{${tokens};background:var(--docs-code-bg)}
|
|
7154
|
+
@media(min-width:1024px){
|
|
7155
|
+
.thally-docs-shell{max-width:none;padding-inline:0}
|
|
7156
|
+
.thally-docs-sidebar,.thally-docs-sidebar>div{width:300px}
|
|
7157
|
+
.thally-docs-sidebar{border-right:1px solid var(--docs-line)}
|
|
7158
|
+
.thally-docs-main{padding-top:50px}
|
|
7159
|
+
.thally-docs-main>div{padding-inline:26px}
|
|
7160
|
+
.thally-docs-article nav[aria-label=Breadcrumb]{display:none}
|
|
7161
|
+
.thally-docs-sidebar nav{padding-inline:16px}
|
|
7162
|
+
.thally-docs-sidebar a[aria-current=page]{background:transparent!important;text-shadow:none}
|
|
7163
|
+
}
|
|
7164
|
+
` };
|
|
6651
7165
|
}
|
|
6652
7166
|
|
|
6653
7167
|
// src/openapi-ref.ts
|
|
@@ -6733,7 +7247,7 @@ function sharedSpecMessage(path, excluded = []) {
|
|
|
6733
7247
|
}
|
|
6734
7248
|
|
|
6735
7249
|
// src/source-refs.ts
|
|
6736
|
-
import { lstatSync as lstatSync5, readdirSync as
|
|
7250
|
+
import { lstatSync as lstatSync5, readdirSync as readdirSync3, readFileSync as readFileSync5, realpathSync as realpathSync3, rmSync, unlinkSync } from "fs";
|
|
6737
7251
|
import { isAbsolute as isAbsolute2, join, relative as relative5, resolve as resolve3, sep as sep2 } from "path";
|
|
6738
7252
|
|
|
6739
7253
|
// src/url.ts
|
|
@@ -8586,7 +9100,7 @@ function sanitizeAndMeasure(root) {
|
|
|
8586
9100
|
const stack = [root];
|
|
8587
9101
|
while (stack.length > 0) {
|
|
8588
9102
|
const directory = stack.pop();
|
|
8589
|
-
for (const entry of
|
|
9103
|
+
for (const entry of readdirSync3(directory, { withFileTypes: true })) {
|
|
8590
9104
|
const path = join(directory, entry.name);
|
|
8591
9105
|
if (entry.isSymbolicLink()) {
|
|
8592
9106
|
unlinkSync(path);
|
|
@@ -8648,8 +9162,8 @@ function empty(mapping, message) {
|
|
|
8648
9162
|
function importSourceRef(mapping, repositoryDir) {
|
|
8649
9163
|
const docsRoot = findSourceRefDocsRoot(repositoryDir);
|
|
8650
9164
|
if (!docsRoot) return empty(mapping, "no Mintlify docs.json was found in docs/ or at the repository root.");
|
|
8651
|
-
const realRoot =
|
|
8652
|
-
const fromClone = relative5(
|
|
9165
|
+
const realRoot = realpathSync3(docsRoot);
|
|
9166
|
+
const fromClone = relative5(realpathSync3(repositoryDir), realRoot);
|
|
8653
9167
|
if (fromClone === ".." || fromClone.startsWith(`..${sep2}`) || isAbsolute2(fromClone)) return empty(mapping, "its docs directory resolves outside the repository.");
|
|
8654
9168
|
const measured = sanitizeAndMeasure(realRoot);
|
|
8655
9169
|
if (measured.error) return empty(mapping, `${measured.error}, over the per-repository limit. Nothing was imported.`);
|
|
@@ -8695,80 +9209,6 @@ function sourceRefMountCollides(siteRoot, mountPath) {
|
|
|
8695
9209
|
});
|
|
8696
9210
|
}
|
|
8697
9211
|
|
|
8698
|
-
// src/css-colors.ts
|
|
8699
|
-
var channel = (value) => Number(value);
|
|
8700
|
-
function cssColorToHex(raw) {
|
|
8701
|
-
const value = raw.trim().replace(/\s*!important$/i, "");
|
|
8702
|
-
if (value.length > 64) return void 0;
|
|
8703
|
-
const hex = /^#([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(value)?.[1];
|
|
8704
|
-
if (hex) return `#${(hex.length === 3 ? [...hex].map((digit) => digit + digit).join("") : hex).toLowerCase()}`;
|
|
8705
|
-
const rgb = /^(?:rgba?\(\s*)?(\d{1,3})(?:\s*,\s*|\s+)(\d{1,3})(?:\s*,\s*|\s+)(\d{1,3})\s*(?:[,/]\s*[\d.]+%?\s*)?\)?$/i.exec(value);
|
|
8706
|
-
if (!rgb) return void 0;
|
|
8707
|
-
const channels = [rgb[1], rgb[2], rgb[3]].map(channel);
|
|
8708
|
-
if (channels.some((entry) => entry > 255)) return void 0;
|
|
8709
|
-
if (value.includes("(") !== value.includes(")")) return void 0;
|
|
8710
|
-
return `#${channels.map((entry) => entry.toString(16).padStart(2, "0")).join("")}`;
|
|
8711
|
-
}
|
|
8712
|
-
function stripComments(css) {
|
|
8713
|
-
let out = "";
|
|
8714
|
-
let index = 0;
|
|
8715
|
-
while (index < css.length) {
|
|
8716
|
-
const open = css.indexOf("/*", index);
|
|
8717
|
-
if (open < 0) break;
|
|
8718
|
-
out += css.slice(index, open);
|
|
8719
|
-
const close = css.indexOf("*/", open + 2);
|
|
8720
|
-
if (close < 0) return out;
|
|
8721
|
-
index = close + 2;
|
|
8722
|
-
}
|
|
8723
|
-
return out + css.slice(index);
|
|
8724
|
-
}
|
|
8725
|
-
function declarations(body, into) {
|
|
8726
|
-
for (const declaration of body.split(";")) {
|
|
8727
|
-
const colon = declaration.indexOf(":");
|
|
8728
|
-
if (colon < 0) continue;
|
|
8729
|
-
const name = declaration.slice(0, colon).trim().toLowerCase();
|
|
8730
|
-
if (name !== "--primary" && name !== "--primary-light" && name !== "--primary-dark") continue;
|
|
8731
|
-
const hex = cssColorToHex(declaration.slice(colon + 1));
|
|
8732
|
-
if (hex) into[name.slice(2)] = hex;
|
|
8733
|
-
}
|
|
8734
|
-
}
|
|
8735
|
-
function cssBrandVars(css) {
|
|
8736
|
-
const source = stripComments(css);
|
|
8737
|
-
const root = {};
|
|
8738
|
-
const dark = {};
|
|
8739
|
-
let index = 0;
|
|
8740
|
-
while (index < source.length) {
|
|
8741
|
-
const open = source.indexOf("{", index);
|
|
8742
|
-
if (open < 0) break;
|
|
8743
|
-
let prelude = (source.slice(index, open).split("}").pop() ?? "").trim();
|
|
8744
|
-
while (prelude.startsWith("@") && prelude.includes(";")) prelude = prelude.slice(prelude.indexOf(";") + 1).trim();
|
|
8745
|
-
let depth = 1;
|
|
8746
|
-
let close = open + 1;
|
|
8747
|
-
for (; close < source.length && depth > 0; close++) {
|
|
8748
|
-
if (source[close] === "{") depth++;
|
|
8749
|
-
else if (source[close] === "}") depth--;
|
|
8750
|
-
}
|
|
8751
|
-
index = close;
|
|
8752
|
-
if (prelude.startsWith("@")) continue;
|
|
8753
|
-
for (const selector of prelude.split(",")) {
|
|
8754
|
-
const name = selector.trim().toLowerCase();
|
|
8755
|
-
const target = name === ":root" || name === "html" ? root : name === ".dark" || name === "html.dark" || name === ":root.dark" ? dark : void 0;
|
|
8756
|
-
if (target) declarations(source.slice(open + 1, close - 1), target);
|
|
8757
|
-
}
|
|
8758
|
-
}
|
|
8759
|
-
return { root, dark };
|
|
8760
|
-
}
|
|
8761
|
-
function brandColorsFromVars({ root, dark }) {
|
|
8762
|
-
const result = {};
|
|
8763
|
-
const lightAccent = root.primary;
|
|
8764
|
-
const lightButton = root["primary-dark"] ?? root.primary;
|
|
8765
|
-
if (lightAccent || lightButton) result.light = { ...lightAccent ? { accent: lightAccent } : {}, ...lightButton ? { primary: lightButton } : {} };
|
|
8766
|
-
const darkAccent = dark["primary-light"] ?? dark.primary;
|
|
8767
|
-
const darkButton = dark["primary-dark"] ?? dark["primary-light"] ?? dark.primary;
|
|
8768
|
-
if (darkAccent || darkButton) result.dark = { ...darkAccent ? { accent: darkAccent } : {}, ...darkButton ? { primary: darkButton } : {} };
|
|
8769
|
-
return result.light || result.dark ? result : void 0;
|
|
8770
|
-
}
|
|
8771
|
-
|
|
8772
9212
|
// src/repository.ts
|
|
8773
9213
|
var MAX_SOURCE_FILES = 5e3;
|
|
8774
9214
|
var MAX_PAGE_BYTES = 2e6;
|
|
@@ -8833,8 +9273,16 @@ var REPOSITORY_ONLY_DOCUMENTS = /* @__PURE__ */ new Set([
|
|
|
8833
9273
|
]);
|
|
8834
9274
|
var SNIPPET_DIRECTORIES = /* @__PURE__ */ new Set(["snippets", "_snippets", "partials", "_partials"]);
|
|
8835
9275
|
var SNIPPET_IMPORT_PATTERN = /\bimport\s+(?:\{\s*(?:default\s+as\s+)?([A-Z][A-Za-z0-9_]*)\s*\}|([A-Z][A-Za-z0-9_]*))\s+from\s+['"]([^'"]+\.mdx?)['"]\s*;?/g;
|
|
9276
|
+
var DOCUSAURUS_GENERATED_EXPORTS = Object.assign(/* @__PURE__ */ Object.create(null), {
|
|
9277
|
+
toc: "[]",
|
|
9278
|
+
frontMatter: "{}",
|
|
9279
|
+
metadata: "{}",
|
|
9280
|
+
assets: "{}",
|
|
9281
|
+
contentTitle: "undefined"
|
|
9282
|
+
});
|
|
8836
9283
|
var SNIPPET_VALUE_IMPORT_PATTERN = /\bimport\s*\{([^{}]+)\}\s*from\s+['"]([^'"]+\.mdx?)['"]\s*;?/g;
|
|
8837
9284
|
var SNIPPET_TAG_PATTERN = /<Snippet\s+file=(?:"([^"]+)"|'([^']+)')\s*(?:\/>|>[\s\S]*?<\/Snippet>)/g;
|
|
9285
|
+
var FERN_MARKDOWN_TAG_PATTERN = /<Markdown\s+src=(?:"([^"]+)"|'([^']+)')\s*(?:\/>|>[\s\S]*?<\/Markdown>)/g;
|
|
8838
9286
|
var MINTIGNORE_FILENAME = ".mintignore";
|
|
8839
9287
|
var createIgnoreMatcher = createRequire(import.meta.url)("ignore");
|
|
8840
9288
|
function isIgnoredDirectory(name) {
|
|
@@ -8879,7 +9327,7 @@ function countMarkdownPages(directory, limit = 2e4) {
|
|
|
8879
9327
|
const current = stack.pop();
|
|
8880
9328
|
let entries;
|
|
8881
9329
|
try {
|
|
8882
|
-
entries =
|
|
9330
|
+
entries = readdirSync4(current, { withFileTypes: true });
|
|
8883
9331
|
} catch {
|
|
8884
9332
|
continue;
|
|
8885
9333
|
}
|
|
@@ -8932,7 +9380,7 @@ function findMintlifyProjectRoot(repositoryDir, docsDir, warnings) {
|
|
|
8932
9380
|
visited++;
|
|
8933
9381
|
if (hasMintlifyConfig(current.directory)) candidates.push(current.directory);
|
|
8934
9382
|
if (current.depth >= 4) continue;
|
|
8935
|
-
for (const entry of
|
|
9383
|
+
for (const entry of readdirSync4(current.directory, { withFileTypes: true })) {
|
|
8936
9384
|
if (!entry.isDirectory() || entry.isSymbolicLink() || isIgnoredDirectory(entry.name)) continue;
|
|
8937
9385
|
queue.push({ directory: resolveWithin(current.directory, entry.name), depth: current.depth + 1 });
|
|
8938
9386
|
}
|
|
@@ -8964,7 +9412,7 @@ function findFernProjectRoot(repositoryDir, docsDir) {
|
|
|
8964
9412
|
visited++;
|
|
8965
9413
|
if (hasFernConfig(current.directory)) return current.directory;
|
|
8966
9414
|
if (current.depth >= 4) continue;
|
|
8967
|
-
for (const entry of
|
|
9415
|
+
for (const entry of readdirSync4(current.directory, { withFileTypes: true })) {
|
|
8968
9416
|
if (!entry.isDirectory() || entry.isSymbolicLink() || isIgnoredDirectory(entry.name)) continue;
|
|
8969
9417
|
queue.push({ directory: resolveWithin(current.directory, entry.name), depth: current.depth + 1 });
|
|
8970
9418
|
}
|
|
@@ -8990,7 +9438,7 @@ function findDocusaurusProjectRoot(repositoryDir, docsDir, warnings) {
|
|
|
8990
9438
|
visited++;
|
|
8991
9439
|
if (hasDocusaurusConfig(current.directory)) candidates.push(current.directory);
|
|
8992
9440
|
if (current.depth >= 4) continue;
|
|
8993
|
-
for (const entry of
|
|
9441
|
+
for (const entry of readdirSync4(current.directory, { withFileTypes: true })) {
|
|
8994
9442
|
if (!entry.isDirectory() || entry.isSymbolicLink() || isIgnoredDirectory(entry.name)) continue;
|
|
8995
9443
|
queue.push({
|
|
8996
9444
|
directory: resolveWithin(current.directory, entry.name),
|
|
@@ -9100,7 +9548,7 @@ function docusaurusArchiveRoots(projectRoot, repositoryDir, warnings) {
|
|
|
9100
9548
|
const directoryNames = (directory) => {
|
|
9101
9549
|
const path = resolveWithin(projectRoot, directory);
|
|
9102
9550
|
if (!existsSync5(path) || !lstatSync6(path).isDirectory()) return [];
|
|
9103
|
-
return
|
|
9551
|
+
return readdirSync4(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map((entry) => entry.name).sort();
|
|
9104
9552
|
};
|
|
9105
9553
|
let versions = directoryNames("versioned_docs").filter((name) => /^version-[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name));
|
|
9106
9554
|
const versionsPath = resolveWithin(projectRoot, "versions.json");
|
|
@@ -9415,7 +9863,7 @@ function detectRepositoryPlatform(repositoryDir, docsDir) {
|
|
|
9415
9863
|
}
|
|
9416
9864
|
function containsMarkdown(directory, depth = 0) {
|
|
9417
9865
|
if (depth > 4) return false;
|
|
9418
|
-
for (const entry of
|
|
9866
|
+
for (const entry of readdirSync4(directory, { withFileTypes: true })) {
|
|
9419
9867
|
if (isIgnoredDirectory(entry.name) || entry.isSymbolicLink()) continue;
|
|
9420
9868
|
const path = resolveWithin(directory, entry.name);
|
|
9421
9869
|
if (entry.isFile() && [".md", ".mdx"].includes(extname6(entry.name).toLowerCase())) return true;
|
|
@@ -9436,7 +9884,7 @@ function scanFiles(root, confinementRoot = root, warnings, rank, budget = MAX_SO
|
|
|
9436
9884
|
const walkCap = rank ? budget * RANKED_WALK_FACTOR : budget;
|
|
9437
9885
|
let confinementReal;
|
|
9438
9886
|
try {
|
|
9439
|
-
confinementReal =
|
|
9887
|
+
confinementReal = realpathSync4(confinementRoot);
|
|
9440
9888
|
} catch {
|
|
9441
9889
|
confinementReal = confinementRoot;
|
|
9442
9890
|
}
|
|
@@ -9445,7 +9893,7 @@ function scanFiles(root, confinementRoot = root, warnings, rank, budget = MAX_SO
|
|
|
9445
9893
|
if (files.length >= walkCap) return;
|
|
9446
9894
|
let directoryReal;
|
|
9447
9895
|
try {
|
|
9448
|
-
directoryReal =
|
|
9896
|
+
directoryReal = realpathSync4(directory);
|
|
9449
9897
|
} catch {
|
|
9450
9898
|
return;
|
|
9451
9899
|
}
|
|
@@ -9453,7 +9901,7 @@ function scanFiles(root, confinementRoot = root, warnings, rank, budget = MAX_SO
|
|
|
9453
9901
|
visitedRealPaths.add(directoryReal);
|
|
9454
9902
|
let entries;
|
|
9455
9903
|
try {
|
|
9456
|
-
entries =
|
|
9904
|
+
entries = readdirSync4(directory, { withFileTypes: true });
|
|
9457
9905
|
} catch {
|
|
9458
9906
|
return;
|
|
9459
9907
|
}
|
|
@@ -9474,7 +9922,7 @@ function scanFiles(root, confinementRoot = root, warnings, rank, budget = MAX_SO
|
|
|
9474
9922
|
if (entry.isSymbolicLink()) {
|
|
9475
9923
|
let real;
|
|
9476
9924
|
try {
|
|
9477
|
-
real =
|
|
9925
|
+
real = realpathSync4(path);
|
|
9478
9926
|
} catch {
|
|
9479
9927
|
continue;
|
|
9480
9928
|
}
|
|
@@ -9601,7 +10049,7 @@ var PATH_SEPARATORS = /[\s"'()<>[\]{}=,;:|\\`*!?#&]+/;
|
|
|
9601
10049
|
function publicAssetKey(repoRelative) {
|
|
9602
10050
|
return repoRelative.split("/", 1)[0].toLowerCase() === "public" ? repoRelative.slice("public/".length) : repoRelative;
|
|
9603
10051
|
}
|
|
9604
|
-
function addPathReferences(text2, baseDir, exact, loose) {
|
|
10052
|
+
function addPathReferences(text2, baseDir, exact, loose, docusaurus) {
|
|
9605
10053
|
for (const word of text2.split(PATH_SEPARATORS)) {
|
|
9606
10054
|
let token = word.replace(/\.+$/, "");
|
|
9607
10055
|
if (!/\.[a-z0-9]{2,5}$/i.test(token) || token.startsWith("//")) continue;
|
|
@@ -9615,14 +10063,40 @@ function addPathReferences(text2, baseDir, exact, loose) {
|
|
|
9615
10063
|
loose.add(token.toLowerCase());
|
|
9616
10064
|
continue;
|
|
9617
10065
|
}
|
|
10066
|
+
if (docusaurus && !token.startsWith("/")) {
|
|
10067
|
+
const fromSite = normalizeAssetPath(posix4.normalize(token.startsWith("@site/") ? token.slice("@site/".length) : posix4.join(docusaurus.siteBase, token)));
|
|
10068
|
+
const docsFolder = docusaurus.docsPrefix ? `${docusaurus.docsPrefix}/` : "";
|
|
10069
|
+
const siteKey = fromSite?.startsWith("static/") ? fromSite.slice("static/".length) : fromSite && docsFolder && fromSite.startsWith(docsFolder) ? fromSite.slice(docsFolder.length) : void 0;
|
|
10070
|
+
if (siteKey) {
|
|
10071
|
+
exact.add(siteKey);
|
|
10072
|
+
loose.add(siteKey.toLowerCase());
|
|
10073
|
+
}
|
|
10074
|
+
if (token.startsWith("@site/")) continue;
|
|
10075
|
+
}
|
|
9618
10076
|
if (!token.startsWith("/") && baseDir === void 0) continue;
|
|
9619
10077
|
const resolved = normalizeAssetPath(posix4.normalize(token.startsWith("/") ? token.slice(1) : posix4.join(baseDir ?? "", token)));
|
|
9620
10078
|
if (!resolved) continue;
|
|
9621
|
-
const key = publicAssetKey(resolved);
|
|
10079
|
+
const key = docusaurus && resolved.startsWith("static/") ? resolved.slice("static/".length) : publicAssetKey(resolved);
|
|
9622
10080
|
exact.add(key);
|
|
9623
10081
|
loose.add(key.toLowerCase());
|
|
9624
10082
|
}
|
|
9625
10083
|
}
|
|
10084
|
+
function addFernConfigPathReferences(config, exact, loose) {
|
|
10085
|
+
const pending = [{ value: config, depth: 0 }];
|
|
10086
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10087
|
+
let visits = 0;
|
|
10088
|
+
while (pending.length > 0 && visits < 5e3) {
|
|
10089
|
+
const { value, depth } = pending.pop();
|
|
10090
|
+
visits++;
|
|
10091
|
+
if (typeof value === "string") {
|
|
10092
|
+
addPathReferences(value, "", exact, loose);
|
|
10093
|
+
} else if (value && typeof value === "object" && !seen.has(value) && depth < 64) {
|
|
10094
|
+
seen.add(value);
|
|
10095
|
+
if (!Array.isArray(value) && fernViewersReason(value)) continue;
|
|
10096
|
+
for (const child of Object.values(value)) pending.push({ value: child, depth: depth + 1 });
|
|
10097
|
+
}
|
|
10098
|
+
}
|
|
10099
|
+
}
|
|
9626
10100
|
function looselyNamed(assetPath, loose) {
|
|
9627
10101
|
const lower = assetPath.toLowerCase();
|
|
9628
10102
|
const name = lower.split("/").pop() ?? lower;
|
|
@@ -9650,6 +10124,18 @@ function classifyApiSpec(path) {
|
|
|
9650
10124
|
return /asyncapi/i.test(topLevel) ? "asyncapi" : "openapi";
|
|
9651
10125
|
}
|
|
9652
10126
|
var SPEC_DIRECTORY = "openapi";
|
|
10127
|
+
var MAX_SPEC_BYTES = 5e7;
|
|
10128
|
+
function readSpecFile(absolutePath, relativePath, warnings) {
|
|
10129
|
+
if (lstatSync6(absolutePath).size > MAX_SPEC_BYTES) {
|
|
10130
|
+
warnings.push({
|
|
10131
|
+
code: "skipped-file",
|
|
10132
|
+
message: `The OpenAPI spec "${relativePath}" is larger than ${MAX_SPEC_BYTES / 1e6} MB and was not migrated; split it or reduce its size.`,
|
|
10133
|
+
source: relativePath
|
|
10134
|
+
});
|
|
10135
|
+
return void 0;
|
|
10136
|
+
}
|
|
10137
|
+
return readFileSync6(absolutePath);
|
|
10138
|
+
}
|
|
9653
10139
|
function specAssetPath(filename) {
|
|
9654
10140
|
return `${SPEC_DIRECTORY}/${filename}`;
|
|
9655
10141
|
}
|
|
@@ -9725,9 +10211,11 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs, p
|
|
|
9725
10211
|
message: `The OpenAPI spec "${reference.value}"${tabSuffix} was limited to pages under "${reference.directory}" in the source, but Thally's API reference always covers a whole tab, so it was migrated as the tab's full API reference. Links in page bodies and Mintlify endpoint URLs under "${reference.directory}/<tag>/<operation>" are redirected to the matching endpoints where one exists (a separate warning counts any endpoint redirects that were skipped); check any other links to "${reference.directory}/..." manually.`
|
|
9726
10212
|
});
|
|
9727
10213
|
}
|
|
10214
|
+
const content = readSpecFile(match.absolutePath, match.relativePath, warnings);
|
|
10215
|
+
if (!content) continue;
|
|
9728
10216
|
specs.push({
|
|
9729
10217
|
filename: basename3(match.relativePath),
|
|
9730
|
-
content
|
|
10218
|
+
content,
|
|
9731
10219
|
tabLabel: reference.tabLabel,
|
|
9732
10220
|
parentTab: reference.parentTab,
|
|
9733
10221
|
icon: reference.icon,
|
|
@@ -9976,7 +10464,7 @@ function findFernConfiguredOpenApi(fernRoot, repositoryDir, apiName, apiNameExpl
|
|
|
9976
10464
|
for (const specPath of fernGeneratorsOpenApiPaths(config)) {
|
|
9977
10465
|
if (/^(?:https?:)?\/\//i.test(specPath)) continue;
|
|
9978
10466
|
try {
|
|
9979
|
-
const absolute = resolveWithinRoot(dir, specPath, repositoryDir);
|
|
10467
|
+
const absolute = withinRealRoot(resolveWithinRoot(dir, specPath, repositoryDir), repositoryDir);
|
|
9980
10468
|
if (!existsSync5(absolute) || !lstatSync6(absolute).isFile()) continue;
|
|
9981
10469
|
const kind = classifyApiSpec(absolute);
|
|
9982
10470
|
if (kind === "asyncapi") {
|
|
@@ -9988,7 +10476,7 @@ function findFernConfiguredOpenApi(fernRoot, repositoryDir, apiName, apiNameExpl
|
|
|
9988
10476
|
} catch {
|
|
9989
10477
|
warnings.push({
|
|
9990
10478
|
code: "unsupported-config",
|
|
9991
|
-
message: `
|
|
10479
|
+
message: `Skipped the OpenAPI spec "${specPath}" listed in ${relative6(repositoryDir, generatorsPath).replace(/\\/g, "/")} because it points outside the repository (symlink or ..).`
|
|
9992
10480
|
});
|
|
9993
10481
|
}
|
|
9994
10482
|
}
|
|
@@ -10027,7 +10515,7 @@ function ownRestrictionReason(access) {
|
|
|
10027
10515
|
}
|
|
10028
10516
|
function canonicalPath(path) {
|
|
10029
10517
|
try {
|
|
10030
|
-
return
|
|
10518
|
+
return realpathSync4.native(path);
|
|
10031
10519
|
} catch {
|
|
10032
10520
|
return path;
|
|
10033
10521
|
}
|
|
@@ -10042,14 +10530,86 @@ function pageHeadAccess(raw) {
|
|
|
10042
10530
|
function describeRestriction(access) {
|
|
10043
10531
|
return access.groups?.length ? `restricted to groups ${access.groups.join(", ")}` : "`public: false`";
|
|
10044
10532
|
}
|
|
10533
|
+
function pageHeadGateReason(raw, platform) {
|
|
10534
|
+
const parsed = parseFrontmatter(raw);
|
|
10535
|
+
const gateKeys = platform === "docusaurus" ? /^draft\s*:/m : /^(viewers|authed)\s*:/m;
|
|
10536
|
+
const unreadable = parsed.error && gateKeys.test(raw.slice(0, raw.length - parsed.content.length)) ? "frontmatter could not be parsed and declares an access or draft setting" : void 0;
|
|
10537
|
+
const openapi = typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0;
|
|
10538
|
+
const reason = platform === "docusaurus" ? docusaurusDraftReason(parsed.data) : fernViewersReason(parsed.data);
|
|
10539
|
+
return { reason: reason ?? unreadable, openapi };
|
|
10540
|
+
}
|
|
10541
|
+
var DOCUSAURUS_DRAFT_REASON = "frontmatter `draft` marks it as a draft Docusaurus does not publish";
|
|
10542
|
+
function docusaurusDraftReason(data) {
|
|
10543
|
+
const draft = data.draft;
|
|
10544
|
+
if (draft === void 0 || draft === false || typeof draft === "string" && draft.trim().toLowerCase() === "false") return void 0;
|
|
10545
|
+
return DOCUSAURUS_DRAFT_REASON;
|
|
10546
|
+
}
|
|
10547
|
+
function withoutFencedCode3(raw) {
|
|
10548
|
+
let fence = null;
|
|
10549
|
+
return raw.split("\n").map((line) => {
|
|
10550
|
+
if (fence) {
|
|
10551
|
+
const closing = /^ {0,3}(`{3,}|~{3,})[ \t]*$/.exec(line);
|
|
10552
|
+
if (closing && closing[1][0] === fence.char && closing[1].length >= fence.length) fence = null;
|
|
10553
|
+
return "";
|
|
10554
|
+
}
|
|
10555
|
+
const opening = /^ {0,3}(`{3,}|~{3,})([^\r]*)$/.exec(line);
|
|
10556
|
+
if (opening && !(opening[1][0] === "`" && opening[2].includes("`"))) {
|
|
10557
|
+
fence = { char: opening[1][0], length: opening[1].length };
|
|
10558
|
+
return "";
|
|
10559
|
+
}
|
|
10560
|
+
return line;
|
|
10561
|
+
}).join("\n");
|
|
10562
|
+
}
|
|
10563
|
+
function fernIfRolesReason(raw) {
|
|
10564
|
+
const text2 = withoutFencedCode3(raw).replace(/(`{1,3})[^`\n]*\1/g, "");
|
|
10565
|
+
const marker = /<If(?![\w$.-])/g;
|
|
10566
|
+
for (let match = marker.exec(text2); match; match = marker.exec(text2)) {
|
|
10567
|
+
const tag = scanJsxOpeningTag(text2, match.index, "If");
|
|
10568
|
+
marker.lastIndex = tag.next;
|
|
10569
|
+
if (tag.attributes.includes("...")) {
|
|
10570
|
+
return "it contains `<If>` spread props whose access rules cannot be resolved safely";
|
|
10571
|
+
}
|
|
10572
|
+
if (tag.attributes.includes("{") && /[/`]/.test(tag.attributes)) {
|
|
10573
|
+
return "it contains `<If>` expression props whose access rules cannot be resolved safely";
|
|
10574
|
+
}
|
|
10575
|
+
if (/(?:^|[\s{}"'])(?:roles|viewers)\s*=/.test(tag.attributes)) {
|
|
10576
|
+
return "it contains `<If roles>` content that Fern shows only to some roles";
|
|
10577
|
+
}
|
|
10578
|
+
}
|
|
10579
|
+
return void 0;
|
|
10580
|
+
}
|
|
10581
|
+
function fernViewersReason(data) {
|
|
10582
|
+
const viewers = data.viewers;
|
|
10583
|
+
const hasViewers = Array.isArray(viewers) ? viewers.length > 0 : typeof viewers === "string" ? viewers.trim() !== "" : viewers !== void 0 && viewers !== null && viewers !== false;
|
|
10584
|
+
if (hasViewers) return "frontmatter `viewers` restricts it to signed-in roles";
|
|
10585
|
+
const authed = data.authed;
|
|
10586
|
+
if (authed !== void 0 && authed !== null && authed !== false && !(typeof authed === "string" && ["", "false"].includes(authed.trim().toLowerCase()))) {
|
|
10587
|
+
return "frontmatter `authed` requires sign-in";
|
|
10588
|
+
}
|
|
10589
|
+
return void 0;
|
|
10590
|
+
}
|
|
10045
10591
|
function withoutFrontmatter(value) {
|
|
10046
10592
|
return value.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
10047
10593
|
}
|
|
10048
10594
|
function staticSnippetProperties(attributes) {
|
|
10049
10595
|
const properties = /* @__PURE__ */ new Map();
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10596
|
+
let tree;
|
|
10597
|
+
try {
|
|
10598
|
+
tree = ts5.createSourceFile("snippet.tsx", `const snippet = <Snippet ${attributes.replace(/\/\s*$/, "")} />`, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
|
|
10599
|
+
} catch {
|
|
10600
|
+
return properties;
|
|
10601
|
+
}
|
|
10602
|
+
const statement = tree.statements[0];
|
|
10603
|
+
if (!statement || !ts5.isVariableStatement(statement)) return properties;
|
|
10604
|
+
const element = statement.declarationList.declarations[0]?.initializer;
|
|
10605
|
+
if (!element || !ts5.isJsxSelfClosingElement(element)) return properties;
|
|
10606
|
+
for (const attribute of element.attributes.properties) {
|
|
10607
|
+
if (!ts5.isJsxAttribute(attribute) || !ts5.isIdentifier(attribute.name)) continue;
|
|
10608
|
+
const value = attribute.initializer;
|
|
10609
|
+
if (value && ts5.isStringLiteral(value)) properties.set(attribute.name.text, value.text);
|
|
10610
|
+
else if (value && ts5.isJsxExpression(value) && value.expression && ts5.isStringLiteralLike(value.expression)) {
|
|
10611
|
+
properties.set(attribute.name.text, value.expression.text);
|
|
10612
|
+
}
|
|
10053
10613
|
}
|
|
10054
10614
|
return properties;
|
|
10055
10615
|
}
|
|
@@ -10057,6 +10617,7 @@ function interpolateSnippet(snippet, attributes, children) {
|
|
|
10057
10617
|
const properties = staticSnippetProperties(attributes);
|
|
10058
10618
|
if (children !== void 0) properties.set("children", children.trim());
|
|
10059
10619
|
if (properties.size === 0) return snippet;
|
|
10620
|
+
snippet = bindSnippetProps(snippet, properties);
|
|
10060
10621
|
const isJsxSnippet = /<[A-Za-z][A-Za-z0-9]*/.test(snippet);
|
|
10061
10622
|
let fence = "";
|
|
10062
10623
|
return snippet.split("\n").map((line) => {
|
|
@@ -10083,18 +10644,18 @@ function isolateFences(snippet, source, start, end) {
|
|
|
10083
10644
|
return `${fence.test(lines[0]) && /\S/.test(before) ? "\n" : ""}${snippet}${fence.test(lines[lines.length - 1]) && /\S/.test(after) ? "\n" : ""}`;
|
|
10084
10645
|
}
|
|
10085
10646
|
function snippetComponentBody(source, componentName) {
|
|
10086
|
-
const parsed =
|
|
10647
|
+
const parsed = ts5.createSourceFile("snippet.tsx", source, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
|
|
10087
10648
|
for (const statement of parsed.statements) {
|
|
10088
|
-
if (!
|
|
10649
|
+
if (!ts5.isVariableStatement(statement)) continue;
|
|
10089
10650
|
for (const declaration of statement.declarationList.declarations) {
|
|
10090
|
-
if (!
|
|
10651
|
+
if (!ts5.isIdentifier(declaration.name) || declaration.name.text !== componentName) continue;
|
|
10091
10652
|
const initializer = declaration.initializer;
|
|
10092
|
-
if (!initializer || !
|
|
10093
|
-
const returned =
|
|
10653
|
+
if (!initializer || !ts5.isArrowFunction(initializer)) continue;
|
|
10654
|
+
const returned = ts5.isBlock(initializer.body) ? initializer.body.statements.find(ts5.isReturnStatement)?.expression : initializer.body;
|
|
10094
10655
|
if (!returned) continue;
|
|
10095
10656
|
let expression = returned;
|
|
10096
|
-
while (
|
|
10097
|
-
if (
|
|
10657
|
+
while (ts5.isParenthesizedExpression(expression)) expression = expression.expression;
|
|
10658
|
+
if (ts5.isJsxElement(expression) || ts5.isJsxSelfClosingElement(expression) || ts5.isJsxFragment(expression)) {
|
|
10098
10659
|
return expression.getText(parsed);
|
|
10099
10660
|
}
|
|
10100
10661
|
}
|
|
@@ -10102,65 +10663,65 @@ function snippetComponentBody(source, componentName) {
|
|
|
10102
10663
|
return source;
|
|
10103
10664
|
}
|
|
10104
10665
|
function statefulSnippetDeclaration(source, componentName) {
|
|
10105
|
-
const parsed =
|
|
10106
|
-
const isEsm = (statement2) =>
|
|
10666
|
+
const parsed = ts5.createSourceFile("snippet.tsx", source, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
|
|
10667
|
+
const isEsm = (statement2) => ts5.isImportDeclaration(statement2) || (ts5.isVariableStatement(statement2) || ts5.isFunctionDeclaration(statement2) || ts5.isClassDeclaration(statement2)) && !!ts5.getModifiers(statement2)?.some((modifier) => modifier.kind === ts5.SyntaxKind.ExportKeyword);
|
|
10107
10668
|
if (parsed.statements.length === 0 || !parsed.statements.every(isEsm)) return null;
|
|
10108
|
-
const statement = parsed.statements.find((candidate) =>
|
|
10109
|
-
if (!statement || !
|
|
10669
|
+
const statement = parsed.statements.find((candidate) => ts5.isVariableStatement(candidate) && candidate.declarationList.declarations.length === 1 && ts5.isIdentifier(candidate.declarationList.declarations[0].name) && candidate.declarationList.declarations[0].name.text === componentName);
|
|
10670
|
+
if (!statement || !ts5.isVariableStatement(statement)) return null;
|
|
10110
10671
|
const declaration = statement.declarationList.declarations[0];
|
|
10111
|
-
if (!declaration.initializer || !
|
|
10672
|
+
if (!declaration.initializer || !ts5.isArrowFunction(declaration.initializer) || !ts5.isBlock(declaration.initializer.body)) return null;
|
|
10112
10673
|
const body = declaration.initializer.body.statements;
|
|
10113
|
-
if (body.length < 2 || !body.some(
|
|
10674
|
+
if (body.length < 2 || !body.some(ts5.isReturnStatement)) return null;
|
|
10114
10675
|
return source.trim();
|
|
10115
10676
|
}
|
|
10116
10677
|
function isStaticLiteral(expression) {
|
|
10117
|
-
if (
|
|
10118
|
-
if (expression.kind ===
|
|
10119
|
-
if (
|
|
10120
|
-
if (
|
|
10121
|
-
return expression.properties.every((property) =>
|
|
10678
|
+
if (ts5.isStringLiteral(expression) || ts5.isNoSubstitutionTemplateLiteral(expression) || ts5.isNumericLiteral(expression)) return true;
|
|
10679
|
+
if (expression.kind === ts5.SyntaxKind.TrueKeyword || expression.kind === ts5.SyntaxKind.FalseKeyword || expression.kind === ts5.SyntaxKind.NullKeyword) return true;
|
|
10680
|
+
if (ts5.isArrayLiteralExpression(expression)) return expression.elements.every(isStaticLiteral);
|
|
10681
|
+
if (ts5.isObjectLiteralExpression(expression)) {
|
|
10682
|
+
return expression.properties.every((property) => ts5.isPropertyAssignment(property) && (ts5.isIdentifier(property.name) || ts5.isStringLiteral(property.name)) && isStaticLiteral(property.initializer));
|
|
10122
10683
|
}
|
|
10123
10684
|
return false;
|
|
10124
10685
|
}
|
|
10125
10686
|
function snippetExportedNames(source) {
|
|
10126
10687
|
const names = /* @__PURE__ */ new Set();
|
|
10127
10688
|
const addBinding = (name) => {
|
|
10128
|
-
if (
|
|
10129
|
-
else for (const element of name.elements) if (!
|
|
10689
|
+
if (ts5.isIdentifier(name)) names.add(name.text);
|
|
10690
|
+
else for (const element of name.elements) if (!ts5.isOmittedExpression(element)) addBinding(element.name);
|
|
10130
10691
|
};
|
|
10131
|
-
const parsed =
|
|
10692
|
+
const parsed = ts5.createSourceFile("snippet.tsx", source, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
|
|
10132
10693
|
for (const statement of parsed.statements) {
|
|
10133
|
-
if (
|
|
10134
|
-
if (statement.exportClause &&
|
|
10694
|
+
if (ts5.isExportDeclaration(statement)) {
|
|
10695
|
+
if (statement.exportClause && ts5.isNamedExports(statement.exportClause)) {
|
|
10135
10696
|
for (const element of statement.exportClause.elements) names.add(element.name.text);
|
|
10136
10697
|
}
|
|
10137
10698
|
continue;
|
|
10138
10699
|
}
|
|
10139
|
-
const modifiers =
|
|
10140
|
-
if (!modifiers?.some((modifier) => modifier.kind ===
|
|
10141
|
-
if (modifiers.some((modifier) => modifier.kind ===
|
|
10142
|
-
if (
|
|
10143
|
-
else if ((
|
|
10700
|
+
const modifiers = ts5.getModifiers(statement);
|
|
10701
|
+
if (!modifiers?.some((modifier) => modifier.kind === ts5.SyntaxKind.ExportKeyword)) continue;
|
|
10702
|
+
if (modifiers.some((modifier) => modifier.kind === ts5.SyntaxKind.DefaultKeyword)) continue;
|
|
10703
|
+
if (ts5.isVariableStatement(statement)) for (const declaration of statement.declarationList.declarations) addBinding(declaration.name);
|
|
10704
|
+
else if ((ts5.isFunctionDeclaration(statement) || ts5.isClassDeclaration(statement)) && statement.name) names.add(statement.name.text);
|
|
10144
10705
|
}
|
|
10145
10706
|
return names;
|
|
10146
10707
|
}
|
|
10147
10708
|
function staticNamedSnippetValues(source) {
|
|
10148
10709
|
const values = /* @__PURE__ */ new Map();
|
|
10149
|
-
const parsed =
|
|
10710
|
+
const parsed = ts5.createSourceFile("snippet.tsx", source, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
|
|
10150
10711
|
for (const statement of parsed.statements) {
|
|
10151
|
-
if (!
|
|
10712
|
+
if (!ts5.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts5.SyntaxKind.ExportKeyword)) continue;
|
|
10152
10713
|
for (const declaration of statement.declarationList.declarations) {
|
|
10153
|
-
if (!
|
|
10714
|
+
if (!ts5.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
10154
10715
|
const expression = declaration.initializer;
|
|
10155
|
-
if (
|
|
10716
|
+
if (ts5.isStringLiteral(expression) || ts5.isNoSubstitutionTemplateLiteral(expression)) {
|
|
10156
10717
|
values.set(declaration.name.text, JSON.stringify(expression.text));
|
|
10157
|
-
} else if (
|
|
10718
|
+
} else if (ts5.isNumericLiteral(expression)) {
|
|
10158
10719
|
values.set(declaration.name.text, expression.text);
|
|
10159
|
-
} else if (expression.kind ===
|
|
10160
|
-
values.set(declaration.name.text, expression.kind ===
|
|
10161
|
-
} else if (/^[a-z_$]/.test(declaration.name.text) && (
|
|
10720
|
+
} else if (expression.kind === ts5.SyntaxKind.TrueKeyword || expression.kind === ts5.SyntaxKind.FalseKeyword) {
|
|
10721
|
+
values.set(declaration.name.text, expression.kind === ts5.SyntaxKind.TrueKeyword ? "true" : "false");
|
|
10722
|
+
} else if (/^[a-z_$]/.test(declaration.name.text) && (ts5.isArrowFunction(expression) || ts5.isFunctionExpression(expression)) && isSelfContainedFunction(expression)) {
|
|
10162
10723
|
values.set(declaration.name.text, expression.getText(parsed));
|
|
10163
|
-
} else if ((
|
|
10724
|
+
} else if ((ts5.isObjectLiteralExpression(expression) || ts5.isArrayLiteralExpression(expression)) && isStaticLiteral(expression)) {
|
|
10164
10725
|
values.set(declaration.name.text, expression.getText(parsed));
|
|
10165
10726
|
}
|
|
10166
10727
|
}
|
|
@@ -10286,6 +10847,14 @@ function preserveLinkedAnchors(pages) {
|
|
|
10286
10847
|
for (const page of pages) {
|
|
10287
10848
|
const fragments = requested.get(page.id);
|
|
10288
10849
|
if (!fragments?.size) continue;
|
|
10850
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
10851
|
+
replaceOutsideCodeAndComments(page.body, (text2) => {
|
|
10852
|
+
for (const match of text2.matchAll(/^ {0,3}\[([^\]\n]+)\]:[^\n]*$/gm)) {
|
|
10853
|
+
const label = match[1].trim().replace(/\s+/g, " ").toLowerCase();
|
|
10854
|
+
if (!definitions.has(label)) definitions.set(label, match[0]);
|
|
10855
|
+
}
|
|
10856
|
+
return text2;
|
|
10857
|
+
});
|
|
10289
10858
|
const lines = page.body.split("\n");
|
|
10290
10859
|
let fence;
|
|
10291
10860
|
const tableCells = [];
|
|
@@ -10300,7 +10869,7 @@ function preserveLinkedAnchors(pages) {
|
|
|
10300
10869
|
const tableCell = line.match(/^ {0,3}\|\s*([^|]+?)\s*\|/);
|
|
10301
10870
|
if (tableCell && !/^[:\s-]+$/.test(tableCell[1])) tableCells.push({ index, text: tableCell[1] });
|
|
10302
10871
|
const match = line.match(/^ {0,3}#{1,6}\s+(.+?)\s*#*\s*$/);
|
|
10303
|
-
return match ? { index, text: match[1].replace(/\s*\{\/\*\s*#\S+?\s*\*\/\}$/, "") } : null;
|
|
10872
|
+
return match ? { index, text: mdxVisibleText(match[1].replace(/\s*\{\/\*\s*#\S+?\s*\*\/\}$/, ""), definitions) } : null;
|
|
10304
10873
|
}).filter((entry) => entry !== null);
|
|
10305
10874
|
const existing = /* @__PURE__ */ new Set();
|
|
10306
10875
|
replaceOutsideCode(page.body, (text2) => {
|
|
@@ -10403,18 +10972,66 @@ function resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot) {
|
|
|
10403
10972
|
resolveWithin(repositoryRoot, relative6(repositoryRoot, candidate));
|
|
10404
10973
|
return withinRealRoot(candidate, repositoryRoot);
|
|
10405
10974
|
}
|
|
10406
|
-
function
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
|
|
10415
|
-
|
|
10975
|
+
function docusaurusGlobalImports(siteRoot, repositoryRoot, warnings) {
|
|
10976
|
+
const registrations = /* @__PURE__ */ new Map();
|
|
10977
|
+
for (const filename of ["MDXComponents.js", "MDXComponents.jsx", "MDXComponents.ts", "MDXComponents.tsx", "MDXComponents/index.js", "MDXComponents/index.tsx"]) {
|
|
10978
|
+
try {
|
|
10979
|
+
const path = withinRealRoot(resolveWithin(siteRoot, `src/theme/${filename}`), repositoryRoot);
|
|
10980
|
+
if (!existsSync5(path) || !lstatSync6(path).isFile()) continue;
|
|
10981
|
+
if (lstatSync6(path).size > MAX_PAGE_BYTES) throw new Error("registry exceeds the 2 MB limit");
|
|
10982
|
+
const tree = ts5.createSourceFile(path, readFileSync6(path, "utf8"), ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
|
|
10983
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
10984
|
+
for (const statement of tree.statements) {
|
|
10985
|
+
if (!ts5.isImportDeclaration(statement) || !ts5.isStringLiteral(statement.moduleSpecifier) || statement.importClause?.isTypeOnly) continue;
|
|
10986
|
+
let source = statement.moduleSpecifier.text;
|
|
10987
|
+
if (source.startsWith(".")) source = `@site/${posix4.normalize(posix4.join("src/theme", posix4.dirname(filename), source))}`;
|
|
10988
|
+
if (!source.startsWith("@site/") && !source.startsWith("/")) continue;
|
|
10989
|
+
const clause = statement.importClause;
|
|
10990
|
+
if (clause?.name) bindings.set(clause.name.text, { source, imported: "default" });
|
|
10991
|
+
if (clause?.namedBindings && ts5.isNamedImports(clause.namedBindings)) {
|
|
10992
|
+
for (const binding of clause.namedBindings.elements) {
|
|
10993
|
+
if (!binding.isTypeOnly) bindings.set(binding.name.text, { source, imported: binding.propertyName?.text ?? binding.name.text });
|
|
10994
|
+
}
|
|
10995
|
+
}
|
|
10996
|
+
}
|
|
10997
|
+
for (const statement of tree.statements) {
|
|
10998
|
+
if (!ts5.isExportAssignment(statement) || statement.isExportEquals || !ts5.isObjectLiteralExpression(statement.expression)) continue;
|
|
10999
|
+
for (const property of statement.expression.properties) {
|
|
11000
|
+
if (!ts5.isShorthandPropertyAssignment(property) && !ts5.isPropertyAssignment(property)) continue;
|
|
11001
|
+
const name = property.name.getText(tree).replace(/^['"]|['"]$/g, "");
|
|
11002
|
+
const local = ts5.isShorthandPropertyAssignment(property) ? property.name : property.initializer;
|
|
11003
|
+
if (!/^[A-Z][\w$]*$/.test(name) || !ts5.isIdentifier(local)) continue;
|
|
11004
|
+
const binding = bindings.get(local.text);
|
|
11005
|
+
if (!binding) continue;
|
|
11006
|
+
if (isThallyBuiltinComponent(name)) {
|
|
11007
|
+
warnings.push({ code: "unsupported-config", message: `Global ${name} override uses the native widget mapping; review the source override for custom styling.`, source: `src/theme/${filename}` });
|
|
11008
|
+
continue;
|
|
11009
|
+
}
|
|
11010
|
+
registrations.set(name, binding.imported === "default" ? `import ${name} from ${JSON.stringify(binding.source)};` : `import { ${binding.imported} as ${name} } from ${JSON.stringify(binding.source)};`);
|
|
11011
|
+
}
|
|
11012
|
+
}
|
|
11013
|
+
return registrations;
|
|
11014
|
+
} catch (error) {
|
|
11015
|
+
warnings.push({ code: "unsupported-config", message: `Docusaurus MDX registry was not imported: ${error instanceof Error ? error.message : "unreadable file"}.`, source: `src/theme/${filename}` });
|
|
11016
|
+
}
|
|
10416
11017
|
}
|
|
10417
|
-
return
|
|
11018
|
+
return registrations;
|
|
11019
|
+
}
|
|
11020
|
+
function injectDocusaurusGlobalImports(raw, imports2) {
|
|
11021
|
+
if (imports2.size === 0) return raw;
|
|
11022
|
+
const parsed = parseFrontmatter(raw);
|
|
11023
|
+
const declared = pageScopeNames([parsed.content]);
|
|
11024
|
+
const used = /* @__PURE__ */ new Set();
|
|
11025
|
+
replaceOutsideCodeAndComments(parsed.content, (segment) => {
|
|
11026
|
+
for (const match of segment.matchAll(/<([A-Z][\w$]*)(?=[\s/>])/g)) {
|
|
11027
|
+
if (imports2.has(match[1]) && !declared.has(match[1])) used.add(match[1]);
|
|
11028
|
+
}
|
|
11029
|
+
return segment;
|
|
11030
|
+
});
|
|
11031
|
+
const prefix = raw.slice(0, raw.length - parsed.content.length);
|
|
11032
|
+
return used.size === 0 ? raw : `${prefix}${[...used].map((name) => imports2.get(name)).join("\n")}
|
|
11033
|
+
|
|
11034
|
+
${parsed.content}`;
|
|
10418
11035
|
}
|
|
10419
11036
|
function globalSnippetAliases(files, repositoryRoot, siteRoot) {
|
|
10420
11037
|
const aliases = /* @__PURE__ */ new Map();
|
|
@@ -10464,8 +11081,8 @@ function snippetExportIndex(files) {
|
|
|
10464
11081
|
continue;
|
|
10465
11082
|
}
|
|
10466
11083
|
if (extension !== ".mdx") {
|
|
10467
|
-
const parsed =
|
|
10468
|
-
const hasDefault = parsed.statements.some((statement) =>
|
|
11084
|
+
const parsed = ts5.createSourceFile("snippet.tsx", source, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
|
|
11085
|
+
const hasDefault = parsed.statements.some((statement) => ts5.isExportAssignment(statement) && !statement.isExportEquals || ts5.getModifiers(statement)?.some((modifier) => modifier.kind === ts5.SyntaxKind.DefaultKeyword));
|
|
10469
11086
|
if (hasDefault) add(basename3(file.relativePath).replace(/\.[^.]+$/, ""), file.absolutePath);
|
|
10470
11087
|
}
|
|
10471
11088
|
}
|
|
@@ -10524,8 +11141,8 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced, onRootF
|
|
|
10524
11141
|
usedRootFallback = true;
|
|
10525
11142
|
}
|
|
10526
11143
|
}
|
|
10527
|
-
const realRoot =
|
|
10528
|
-
const realCandidate =
|
|
11144
|
+
const realRoot = realpathSync4(siteRoot);
|
|
11145
|
+
const realCandidate = realpathSync4(candidate);
|
|
10529
11146
|
const realRelative = relative6(realRoot, realCandidate);
|
|
10530
11147
|
if (realRelative === ".." || realRelative.startsWith(`..${sep3}`) || isAbsolute3(realRelative)) return null;
|
|
10531
11148
|
const siteRelative = relative6(siteRoot, candidate).replace(/\\/g, "/");
|
|
@@ -10533,7 +11150,7 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced, onRootF
|
|
|
10533
11150
|
if (!existsSync5(candidate) || !lstatSync6(candidate).isFile()) return null;
|
|
10534
11151
|
const normalized = normalizeAssetPath(siteRelative);
|
|
10535
11152
|
if (!normalized) return null;
|
|
10536
|
-
const onDiskRelative = relative6(realRoot,
|
|
11153
|
+
const onDiskRelative = relative6(realRoot, realpathSync4.native(candidate)).replace(/\\/g, "/");
|
|
10537
11154
|
const onDisk = onDiskRelative.toLowerCase() === siteRelative.toLowerCase() ? normalizeAssetPath(onDiskRelative) : null;
|
|
10538
11155
|
onReferenced?.(normalized, onDisk ?? void 0);
|
|
10539
11156
|
if (usedRootFallback) onRootFallback?.(decodedPath, normalized);
|
|
@@ -10594,7 +11211,13 @@ function locallyDeclaredNames(raw) {
|
|
|
10594
11211
|
}
|
|
10595
11212
|
return names;
|
|
10596
11213
|
}
|
|
10597
|
-
function
|
|
11214
|
+
function exportBlock(declarations2, statement, sourcePath) {
|
|
11215
|
+
const code = declarations2.join("\n");
|
|
11216
|
+
return code && statement.slice(statement.lastIndexOf(sourcePath)).includes("\n") ? `${code}
|
|
11217
|
+
|
|
11218
|
+
` : code;
|
|
11219
|
+
}
|
|
11220
|
+
function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0, siteRoot = repositoryRoot, globalAliases = /* @__PURE__ */ new Map(), gate, platform) {
|
|
10598
11221
|
if (depth >= 8) return raw;
|
|
10599
11222
|
raw = normalizeIndentedFences(raw);
|
|
10600
11223
|
const snippets = /* @__PURE__ */ new Map();
|
|
@@ -10608,6 +11231,10 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10608
11231
|
warnings.push({ code: "gated-page", message: `${label} is access-restricted and was NOT inlined; it was left as a comment.`, source });
|
|
10609
11232
|
return `{/* Access-restricted content withheld: ${shown} */}`;
|
|
10610
11233
|
}
|
|
11234
|
+
if (verdict.kind === "draft") {
|
|
11235
|
+
warnings.push({ code: "gated-page", message: `${label} is a draft page that Docusaurus does not publish, so it was not inlined; it was left as a comment.`, source });
|
|
11236
|
+
return `{/* Draft content not inlined: ${shown} */}`;
|
|
11237
|
+
}
|
|
10611
11238
|
if (verdict.kind === "snippet-gated") {
|
|
10612
11239
|
warnings.push({
|
|
10613
11240
|
code: "gated-page",
|
|
@@ -10623,6 +11250,59 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10623
11250
|
});
|
|
10624
11251
|
return `{/* ${verdict.kind === "oversized" ? "Oversized" : "Unreadable"} content not inlined: ${shown} */}`;
|
|
10625
11252
|
};
|
|
11253
|
+
if (platform === "fern") raw = replaceOutsideCodeAndComments(raw, (segment) => {
|
|
11254
|
+
const marker = /<Code(?![\w$.-])/g;
|
|
11255
|
+
let output = "";
|
|
11256
|
+
let cursor = 0;
|
|
11257
|
+
for (let match = marker.exec(segment); match; match = marker.exec(segment)) {
|
|
11258
|
+
const opening = scanJsxOpeningTag(segment, match.index, "Code");
|
|
11259
|
+
marker.lastIndex = opening.next;
|
|
11260
|
+
if (opening.end === null) continue;
|
|
11261
|
+
if (segment[opening.end - 1] !== "/") continue;
|
|
11262
|
+
const properties = staticSnippetProperties(opening.attributes);
|
|
11263
|
+
const attribute = (name) => properties.get(name);
|
|
11264
|
+
const filePath = attribute("src");
|
|
11265
|
+
if (!filePath) continue;
|
|
11266
|
+
const end = opening.end + 1;
|
|
11267
|
+
let replacement;
|
|
11268
|
+
try {
|
|
11269
|
+
const candidate = resolveSnippetPath(filePath, currentFile, repositoryRoot, siteRoot);
|
|
11270
|
+
if (!existsSync5(candidate) || !lstatSync6(candidate).isFile()) throw new Error("file not found");
|
|
11271
|
+
if (lstatSync6(candidate).size > MAX_PAGE_BYTES) throw new Error("file exceeds the 2 MB inline limit");
|
|
11272
|
+
const blocked = blockInline(candidate, filePath, `Code source ${filePath}`);
|
|
11273
|
+
if (blocked) replacement = blocked;
|
|
11274
|
+
else {
|
|
11275
|
+
let code = readFileSync6(candidate, "utf8").replace(/\r\n/g, "\n");
|
|
11276
|
+
const lines = attribute("lines");
|
|
11277
|
+
if (lines !== void 0) {
|
|
11278
|
+
const range = /^(\d+)(?:-(\d+))?$/.exec(lines);
|
|
11279
|
+
const first = Number(range?.[1]);
|
|
11280
|
+
const last = Number(range?.[2] ?? range?.[1]);
|
|
11281
|
+
if (!Number.isSafeInteger(first) || !Number.isSafeInteger(last) || first < 1 || last < first) throw new Error("invalid line range");
|
|
11282
|
+
code = code.split("\n").slice(first - 1, last).join("\n");
|
|
11283
|
+
}
|
|
11284
|
+
const language = attribute("language") ?? "text";
|
|
11285
|
+
if (!/^[\w+-]+$/.test(language)) throw new Error("invalid language");
|
|
11286
|
+
const title = attribute("title")?.replace(/[\r\n`]/g, " ");
|
|
11287
|
+
let fenceLength = 3;
|
|
11288
|
+
for (const run of code.matchAll(/`+/g)) fenceLength = Math.max(fenceLength, run[0].length + 1);
|
|
11289
|
+
const fence = "`".repeat(fenceLength);
|
|
11290
|
+
const before = segment.slice(segment.lastIndexOf("\n", match.index - 1) + 1, match.index);
|
|
11291
|
+
const indent = /^[ \t]*$/.test(before) ? before : "";
|
|
11292
|
+
replacement = isolateFences(`${fence}${language}${title ? ` ${title}` : ""}
|
|
11293
|
+
${code.replace(/\n$/, "")}
|
|
11294
|
+
${fence}`.split("\n").join(`
|
|
11295
|
+
${indent}`), segment, match.index, end);
|
|
11296
|
+
}
|
|
11297
|
+
} catch (error) {
|
|
11298
|
+
warnings.push({ code: "skipped-file", message: `Code source ${filePath} was not inlined: ${error instanceof Error ? error.message : "unreadable file"}.`, source });
|
|
11299
|
+
replacement = mdxComment(` Code source not inlined: ${filePath} `);
|
|
11300
|
+
}
|
|
11301
|
+
output += segment.slice(cursor, match.index) + replacement;
|
|
11302
|
+
cursor = end;
|
|
11303
|
+
}
|
|
11304
|
+
return output + segment.slice(cursor);
|
|
11305
|
+
});
|
|
10626
11306
|
let withoutImports = replaceOutsideCodeAndMdxComments(raw, (source2) => source2.replace(
|
|
10627
11307
|
SNIPPET_IMPORT_PATTERN,
|
|
10628
11308
|
(_statement, namedComponent, defaultComponent, sourcePath) => {
|
|
@@ -10644,7 +11324,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10644
11324
|
depth + 1,
|
|
10645
11325
|
siteRoot,
|
|
10646
11326
|
globalAliases,
|
|
10647
|
-
gate
|
|
11327
|
+
gate,
|
|
11328
|
+
platform
|
|
10648
11329
|
);
|
|
10649
11330
|
const declaration = statefulSnippetDeclaration(nested, componentName);
|
|
10650
11331
|
if (declaration) preservedDeclarations.set(componentName, declaration);
|
|
@@ -10692,7 +11373,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10692
11373
|
if (/^[A-Z]/.test(binding.exported)) snippets.set(binding.local, blocked);
|
|
10693
11374
|
else declarations3.push(`export const ${binding.local} = undefined;`);
|
|
10694
11375
|
}
|
|
10695
|
-
return declarations3
|
|
11376
|
+
return exportBlock(declarations3, statement, sourcePath);
|
|
10696
11377
|
}
|
|
10697
11378
|
const snippetSource = readFileSync6(candidate, "utf8");
|
|
10698
11379
|
const values = staticNamedSnippetValues(snippetSource);
|
|
@@ -10707,6 +11388,11 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10707
11388
|
}
|
|
10708
11389
|
exportedNames ??= snippetExportedNames(snippetSource);
|
|
10709
11390
|
const exported = exportedNames.has(binding.exported);
|
|
11391
|
+
const generated = exported || platform !== "docusaurus" ? void 0 : DOCUSAURUS_GENERATED_EXPORTS[binding.exported];
|
|
11392
|
+
if (generated) {
|
|
11393
|
+
declarations2.push(`export const ${binding.local} = ${generated};`);
|
|
11394
|
+
continue;
|
|
11395
|
+
}
|
|
10710
11396
|
if (!exported && !(/^[A-Z]/.test(binding.exported) && snippetComponentBody(snippetSource, binding.exported) !== snippetSource)) {
|
|
10711
11397
|
warnings.push({
|
|
10712
11398
|
code: "unsupported-config",
|
|
@@ -10730,7 +11416,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10730
11416
|
components.push([binding.local, body]);
|
|
10731
11417
|
}
|
|
10732
11418
|
for (const [name, body] of components) snippets.set(name, body);
|
|
10733
|
-
return declarations2
|
|
11419
|
+
return exportBlock(declarations2, statement, sourcePath);
|
|
10734
11420
|
} catch (error) {
|
|
10735
11421
|
if (error instanceof Error && error.message.includes("escapes its root")) {
|
|
10736
11422
|
warnings.push({
|
|
@@ -10764,7 +11450,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10764
11450
|
depth + 1,
|
|
10765
11451
|
siteRoot,
|
|
10766
11452
|
globalAliases,
|
|
10767
|
-
gate
|
|
11453
|
+
gate,
|
|
11454
|
+
platform
|
|
10768
11455
|
);
|
|
10769
11456
|
const declaration = statefulSnippetDeclaration(nested, componentName);
|
|
10770
11457
|
if (declaration) preservedDeclarations.set(componentName, declaration);
|
|
@@ -10778,13 +11465,17 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10778
11465
|
return isolateFences(closeOpenCodeFence(interpolateSnippet(snippet, attributes, children)), source2, offset, offset + tag.length);
|
|
10779
11466
|
});
|
|
10780
11467
|
}
|
|
10781
|
-
|
|
11468
|
+
const tagLabel = (filePath) => platform === "fern" ? `Included file ${filePath}` : `Snippet file="${filePath}"`;
|
|
11469
|
+
return segment.replace(platform === "fern" ? FERN_MARKDOWN_TAG_PATTERN : SNIPPET_TAG_PATTERN, (tag, doubleQuoted, singleQuoted, offset, source2) => {
|
|
10782
11470
|
const filePath = doubleQuoted ?? singleQuoted;
|
|
10783
11471
|
try {
|
|
10784
|
-
const candidate = [
|
|
11472
|
+
const candidate = (platform === "fern" ? [
|
|
11473
|
+
// Fern's absolute include paths are rooted at fern/, never snippets/.
|
|
11474
|
+
() => resolveSnippetPath(filePath, currentFile, repositoryRoot, siteRoot)
|
|
11475
|
+
] : [
|
|
10785
11476
|
() => withinRealRoot(resolveWithin(siteRoot, `snippets/${filePath}`), repositoryRoot),
|
|
10786
11477
|
() => resolveSnippetPath(filePath, currentFile, repositoryRoot, siteRoot)
|
|
10787
|
-
].map((resolveCandidate) => {
|
|
11478
|
+
]).map((resolveCandidate) => {
|
|
10788
11479
|
try {
|
|
10789
11480
|
return resolveCandidate();
|
|
10790
11481
|
} catch {
|
|
@@ -10792,7 +11483,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10792
11483
|
}
|
|
10793
11484
|
}).find((path) => path !== void 0 && existsSync5(path) && lstatSync6(path).isFile());
|
|
10794
11485
|
if (!candidate) throw new Error("file not found");
|
|
10795
|
-
const blocked = blockInline(candidate, filePath,
|
|
11486
|
+
const blocked = blockInline(candidate, filePath, tagLabel(filePath));
|
|
10796
11487
|
if (blocked) return blocked;
|
|
10797
11488
|
return isolateFences(closeOpenCodeFence(inlineMdxSnippets(
|
|
10798
11489
|
withoutFrontmatter(readFileSync6(candidate, "utf8")),
|
|
@@ -10802,12 +11493,13 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
10802
11493
|
depth + 1,
|
|
10803
11494
|
siteRoot,
|
|
10804
11495
|
globalAliases,
|
|
10805
|
-
gate
|
|
11496
|
+
gate,
|
|
11497
|
+
platform
|
|
10806
11498
|
)), source2, offset, offset + tag.length);
|
|
10807
11499
|
} catch {
|
|
10808
11500
|
warnings.push({
|
|
10809
11501
|
code: "missing-page",
|
|
10810
|
-
message:
|
|
11502
|
+
message: platform === "fern" ? `${tagLabel(filePath)} could not be found, so it was left out of the page.` : `${tagLabel(filePath)} could not be resolved and was left as a comment.`,
|
|
10811
11503
|
source: relative6(repositoryRoot, currentFile).replace(/\\/g, "/")
|
|
10812
11504
|
});
|
|
10813
11505
|
return mdxComment(` Missing snippet: ${filePath} `);
|
|
@@ -10890,6 +11582,7 @@ function migrateRepository(options) {
|
|
|
10890
11582
|
})();
|
|
10891
11583
|
const componentRoot = mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir;
|
|
10892
11584
|
const componentMigrator = platform === "mintlify" || platform === "docusaurus" || platform === "fern" ? createComponentMigrator(componentRoot, repositoryDir, warnings, componentSourceIdentity(options.sourceUrl, repositoryDir, componentRoot, options.componentNamespace)) : void 0;
|
|
11585
|
+
const globalDocusaurusImports = platform === "docusaurus" ? docusaurusGlobalImports(componentRoot, repositoryDir, warnings) : /* @__PURE__ */ new Map();
|
|
10893
11586
|
let docsConfig = { tabs: [] };
|
|
10894
11587
|
let defaultVersionPrefixes = /* @__PURE__ */ new Set();
|
|
10895
11588
|
let allVersionPrefixes = /* @__PURE__ */ new Set();
|
|
@@ -10903,12 +11596,15 @@ function migrateRepository(options) {
|
|
|
10903
11596
|
let fernBasePath = "";
|
|
10904
11597
|
let fernVersionPath = "";
|
|
10905
11598
|
let fernApiSections = [];
|
|
11599
|
+
let fernRestrictedApiSections = [];
|
|
11600
|
+
let fernNavigationIncomplete = false;
|
|
10906
11601
|
const fernExternalSourcePaths = /* @__PURE__ */ new Set();
|
|
10907
11602
|
let fernChangelogIndexes = [];
|
|
10908
11603
|
const fernReferencedPaths = /* @__PURE__ */ new Set();
|
|
10909
11604
|
const fernSourceLinkAliases = /* @__PURE__ */ new Map();
|
|
10910
11605
|
const fernNavTitles = /* @__PURE__ */ new Map();
|
|
10911
11606
|
const fernHiddenIds = /* @__PURE__ */ new Set();
|
|
11607
|
+
const fernRestrictedPaths = /* @__PURE__ */ new Set();
|
|
10912
11608
|
const activeSourceRefs = [];
|
|
10913
11609
|
const usedSourceRefs = /* @__PURE__ */ new Set();
|
|
10914
11610
|
const gatedSourceRepos = /* @__PURE__ */ new Set();
|
|
@@ -10971,6 +11667,8 @@ function migrateRepository(options) {
|
|
|
10971
11667
|
docsConfig = projected.docsConfig;
|
|
10972
11668
|
warnings.push(...projected.warnings);
|
|
10973
11669
|
fernApiSections = projected.apiSections;
|
|
11670
|
+
fernRestrictedApiSections = projected.restrictedApiSections;
|
|
11671
|
+
fernNavigationIncomplete = projected.navigationIncomplete;
|
|
10974
11672
|
fernChangelogIndexes = projected.changelogIndexes;
|
|
10975
11673
|
for (const [index, descriptor] of projected.descriptors.entries()) {
|
|
10976
11674
|
fernReferencedPaths.add(descriptor.sourcePath);
|
|
@@ -10978,6 +11676,7 @@ function migrateRepository(options) {
|
|
|
10978
11676
|
fernNavTitles.set(descriptor.navigationId, descriptor.navTitle);
|
|
10979
11677
|
}
|
|
10980
11678
|
if (descriptor.hidden) fernHiddenIds.add(descriptor.navigationId);
|
|
11679
|
+
if (descriptor.restricted) fernRestrictedPaths.add(canonicalPath(resolvePath(fernProjectRoot, descriptor.sourcePath)));
|
|
10981
11680
|
const sourceTail = descriptor.sourcePath.match(/(?:^|\/)pages\/(.+)\.mdx?$/i)?.[1]?.replace(/\/index$/i, "");
|
|
10982
11681
|
if (sourceTail) {
|
|
10983
11682
|
const routeSegments = descriptor.navigationId.split("/");
|
|
@@ -11002,13 +11701,17 @@ function migrateRepository(options) {
|
|
|
11002
11701
|
});
|
|
11003
11702
|
}
|
|
11004
11703
|
}
|
|
11704
|
+
if (fernNavigationIncomplete) {
|
|
11705
|
+
throw new Error(`Fern navigation could not be fully classified, so no pages, assets or API specs were migrated. Fix cyclic references or reduce the navigation's nesting or node count before retrying.
|
|
11706
|
+
${warnings.map((warning) => `- ${warning.message}`).join("\n")}`);
|
|
11707
|
+
}
|
|
11005
11708
|
if (platform === "docusaurus") {
|
|
11006
11709
|
try {
|
|
11007
11710
|
docusaurusSidebars = options.docusaurusSkipSidebar || !docusaurusProjectRoot ? null : readDocusaurusSidebars(docusaurusProjectRoot, options.docusaurusSidebarPath);
|
|
11008
11711
|
} catch (error) {
|
|
11009
11712
|
warnings.push({
|
|
11010
11713
|
code: "unsupported-config",
|
|
11011
|
-
message: `Docusaurus sidebar could not be read safely: ${error instanceof Error ? error.message : String(error)} Generated navigation will be used.`
|
|
11714
|
+
message: isPathEscapeError(error) ? "Skipped the sidebar file because it points outside the repository (symlink or ..). Navigation was generated from the docs folder instead." : `Docusaurus sidebar could not be read safely: ${error instanceof Error ? error.message : String(error)} Generated navigation will be used.`
|
|
11012
11715
|
});
|
|
11013
11716
|
}
|
|
11014
11717
|
}
|
|
@@ -11040,7 +11743,7 @@ function migrateRepository(options) {
|
|
|
11040
11743
|
for (const sourcePath of fernExternalSourcePaths) {
|
|
11041
11744
|
if (discovered2.has(sourcePath)) continue;
|
|
11042
11745
|
try {
|
|
11043
|
-
const absolutePath = resolveWithinRoot(fernProjectRoot, sourcePath, repositoryDir);
|
|
11746
|
+
const absolutePath = withinRealRoot(resolveWithinRoot(fernProjectRoot, sourcePath, repositoryDir), repositoryDir);
|
|
11044
11747
|
if (existsSync5(absolutePath) && lstatSync6(absolutePath).isFile()) {
|
|
11045
11748
|
files.push({ absolutePath, relativePath: sourcePath });
|
|
11046
11749
|
discovered2.add(sourcePath);
|
|
@@ -11088,10 +11791,11 @@ function migrateRepository(options) {
|
|
|
11088
11791
|
const size = lstatSync6(file.absolutePath).size;
|
|
11089
11792
|
const raw = readFrontmatterHead(file.absolutePath) ?? (size > MAX_PAGE_BYTES ? void 0 : readFileSync6(file.absolutePath, "utf8"));
|
|
11090
11793
|
if (raw === void 0) throw new Error("frontmatter is not terminated within the bounded read");
|
|
11091
|
-
head = pageHeadAccess(raw);
|
|
11794
|
+
head = platform === "mintlify" ? pageHeadAccess(raw) : pageHeadGateReason(raw, platform);
|
|
11092
11795
|
} catch {
|
|
11093
|
-
head = { access: { problem: "frontmatter could not be read" } };
|
|
11796
|
+
head = platform === "mintlify" ? { access: { problem: "frontmatter could not be read" } } : { reason: "frontmatter could not be read" };
|
|
11094
11797
|
}
|
|
11798
|
+
if (!("access" in head)) return head.reason ? { reason: head.reason, quarantineReason: head.reason, openapi: head.openapi } : { openapi: head.openapi };
|
|
11095
11799
|
const verdict = resolvePageAccess(head.access, navigationAccessFor(file.relativePath));
|
|
11096
11800
|
if (verdict.problem) return { reason: verdict.problem, quarantineReason: verdict.problem, openapi: head.openapi };
|
|
11097
11801
|
const access = verdict.access ?? {};
|
|
@@ -11144,9 +11848,38 @@ function migrateRepository(options) {
|
|
|
11144
11848
|
withheldDocFiles.push(file);
|
|
11145
11849
|
}
|
|
11146
11850
|
}
|
|
11147
|
-
const
|
|
11851
|
+
const fernBodyReason = (file) => {
|
|
11852
|
+
if (platform !== "fern") return void 0;
|
|
11853
|
+
try {
|
|
11854
|
+
return lstatSync6(file.absolutePath).size > MAX_PAGE_BYTES ? void 0 : fernIfRolesReason(readFileSync6(file.absolutePath, "utf8"));
|
|
11855
|
+
} catch {
|
|
11856
|
+
return "it could not be read to check for role-restricted content";
|
|
11857
|
+
}
|
|
11858
|
+
};
|
|
11859
|
+
const gateReasonCache = /* @__PURE__ */ new Map();
|
|
11860
|
+
const nonMintlifyGateReason = (file) => {
|
|
11861
|
+
if (!isDocFile(file)) return void 0;
|
|
11862
|
+
const key = canonicalPath(file.absolutePath);
|
|
11863
|
+
let reason = gateReasonCache.get(key);
|
|
11864
|
+
if (reason === void 0) {
|
|
11865
|
+
reason = platform === "docusaurus" && file.relativePath.split("/").some((segment) => segment.startsWith("_")) ? null : (fernRestrictedPaths.has(key) ? "docs.yml `viewers` restricts it to signed-in roles" : classifyPageGate(file).reason ?? fernBodyReason(file)) ?? null;
|
|
11866
|
+
gateReasonCache.set(key, reason);
|
|
11867
|
+
}
|
|
11868
|
+
return reason ?? void 0;
|
|
11869
|
+
};
|
|
11870
|
+
if (platform === "docusaurus" || platform === "fern") {
|
|
11871
|
+
for (const file of /* @__PURE__ */ new Set([...scannedFiles, ...files])) {
|
|
11872
|
+
const reason = nonMintlifyGateReason(file);
|
|
11873
|
+
if (!reason) continue;
|
|
11874
|
+
gateByPath.set(file.absolutePath, { reason, quarantineReason: reason });
|
|
11875
|
+
withheldPaths.add(file.absolutePath);
|
|
11876
|
+
}
|
|
11877
|
+
}
|
|
11878
|
+
const pagesNotClassified = droppedPageCount > 0 || scanTruncated || files.some((file) => isDocFile(file) && lstatSync6(file.absolutePath).size > MAX_PAGE_BYTES);
|
|
11148
11879
|
const hasWithheldContent = withheldPaths.size > 0 || withheldDocFiles.length > 0;
|
|
11149
|
-
const trackPublishedRefs =
|
|
11880
|
+
const trackPublishedRefs = hasWithheldContent || pagesNotClassified;
|
|
11881
|
+
const docusaurusDocsPrefix = platform === "docusaurus" ? relative6(docusaurusProjectRoot ?? repositoryDir, contentRoot).replace(/\\/g, "/") : "";
|
|
11882
|
+
const docusaurusPaths = (siteBase) => platform === "docusaurus" ? { siteBase: posix4.normalize(siteBase).replace(/^\.$/, ""), docsPrefix: docusaurusDocsPrefix } : void 0;
|
|
11150
11883
|
const withheldCanonical = new Set([...withheldPaths].map(canonicalPath));
|
|
11151
11884
|
const verdictCache = /* @__PURE__ */ new Map();
|
|
11152
11885
|
const classifyInlineCandidate = (candidate, key) => {
|
|
@@ -11177,7 +11910,11 @@ function migrateRepository(options) {
|
|
|
11177
11910
|
verdictCache.set(key, verdict);
|
|
11178
11911
|
}
|
|
11179
11912
|
return verdict ?? void 0;
|
|
11180
|
-
} :
|
|
11913
|
+
} : (candidate) => {
|
|
11914
|
+
const reason = nonMintlifyGateReason({ absolutePath: candidate, relativePath: relative6(contentRoot, candidate).replace(/\\/g, "/") });
|
|
11915
|
+
if (reason) return { kind: reason === DOCUSAURUS_DRAFT_REASON ? "draft" : "gated", reason };
|
|
11916
|
+
return lstatSync6(candidate).size > MAX_PAGE_BYTES ? { kind: "oversized", reason: "over 2 MB" } : void 0;
|
|
11917
|
+
};
|
|
11181
11918
|
const fernIdRenames = /* @__PURE__ */ new Map();
|
|
11182
11919
|
let skipped = 0;
|
|
11183
11920
|
let discovered = files.length;
|
|
@@ -11239,7 +11976,7 @@ function migrateRepository(options) {
|
|
|
11239
11976
|
continue;
|
|
11240
11977
|
}
|
|
11241
11978
|
const sourceSegments = file.relativePath.split("/");
|
|
11242
|
-
if (sourceSegments.some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase())) || platform === "docusaurus" &&
|
|
11979
|
+
if (sourceSegments.some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase())) || platform === "docusaurus" && sourceSegments.some((segment) => segment.startsWith("_"))) {
|
|
11243
11980
|
skipped++;
|
|
11244
11981
|
continue;
|
|
11245
11982
|
}
|
|
@@ -11280,7 +12017,7 @@ function migrateRepository(options) {
|
|
|
11280
12017
|
}
|
|
11281
12018
|
const key = normalizedReferenceKey(file.relativePath);
|
|
11282
12019
|
let pageGate;
|
|
11283
|
-
|
|
12020
|
+
{
|
|
11284
12021
|
pageGate = gateByPath.get(file.absolutePath);
|
|
11285
12022
|
if (pageGate?.access?.isPublic === true) sawPublicTrue = true;
|
|
11286
12023
|
const gateReason = pageGate?.quarantineReason;
|
|
@@ -11310,7 +12047,7 @@ function migrateRepository(options) {
|
|
|
11310
12047
|
}
|
|
11311
12048
|
warnings.push({
|
|
11312
12049
|
code: "gated-page",
|
|
11313
|
-
message: `Access-restricted on the source site, but its access rules could not be migrated safely (${gateReason}), so it was NOT published. Fix the rules in the original, saved at ${QUARANTINE_DIRECTORY}/${file.relativePath}, and move it into src/content/; until then links from other pages to it will break.`,
|
|
12050
|
+
message: gateReason === DOCUSAURUS_DRAFT_REASON ? `This is a draft page that Docusaurus does not publish, so it was NOT published. The original is saved at ${QUARANTINE_DIRECTORY}/${file.relativePath}; links from other pages to it will break.` : platform === "mintlify" ? `Access-restricted on the source site, but its access rules could not be migrated safely (${gateReason}), so it was NOT published. Fix the rules in the original, saved at ${QUARANTINE_DIRECTORY}/${file.relativePath}, and move it into src/content/; until then links from other pages to it will break.` : `Access-restricted on the source site (${gateReason}), so it was NOT published. The original is saved at ${QUARANTINE_DIRECTORY}/${file.relativePath}; links from other pages to it will break.`,
|
|
11314
12051
|
source: file.relativePath
|
|
11315
12052
|
});
|
|
11316
12053
|
continue;
|
|
@@ -11348,14 +12085,15 @@ function migrateRepository(options) {
|
|
|
11348
12085
|
const isDefaultLocale = !locale || locale === localeConfig?.defaultLocale;
|
|
11349
12086
|
const id = isDefaultLocale ? navigationId : `${locale}/${navigationId}`;
|
|
11350
12087
|
let raw = inlineMdxSnippets(
|
|
11351
|
-
mintlifyProjectRoot ? importUndeclaredSnippetComponents(readFileSync6(file.absolutePath, "utf8"), file.absolutePath, repositoryDir, mintlifyProjectRoot, snippetExports, snippetAliases, inlineGate, warnings) : readFileSync6(file.absolutePath, "utf8"),
|
|
12088
|
+
mintlifyProjectRoot ? importUndeclaredSnippetComponents(readFileSync6(file.absolutePath, "utf8"), file.absolutePath, repositoryDir, mintlifyProjectRoot, snippetExports, snippetAliases, inlineGate, warnings) : injectDocusaurusGlobalImports(readFileSync6(file.absolutePath, "utf8"), globalDocusaurusImports),
|
|
11352
12089
|
file.absolutePath,
|
|
11353
12090
|
repositoryDir,
|
|
11354
12091
|
warnings,
|
|
11355
12092
|
0,
|
|
11356
12093
|
mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir,
|
|
11357
12094
|
snippetAliases,
|
|
11358
|
-
inlineGate
|
|
12095
|
+
inlineGate,
|
|
12096
|
+
platform
|
|
11359
12097
|
);
|
|
11360
12098
|
const publishedText = trackPublishedRefs && !isRestrictedPage ? raw : void 0;
|
|
11361
12099
|
const pageAssetReferences = [];
|
|
@@ -11476,6 +12214,7 @@ function migrateRepository(options) {
|
|
|
11476
12214
|
});
|
|
11477
12215
|
});
|
|
11478
12216
|
}
|
|
12217
|
+
if (platform === "docusaurus") resolvePageDisplayHeading(page, platform);
|
|
11479
12218
|
let mdxError = invalidMdxReason(page.body);
|
|
11480
12219
|
if (mdxError) {
|
|
11481
12220
|
const repaired = escapeUnmatchedClosingTags(page.body);
|
|
@@ -11543,7 +12282,7 @@ function migrateRepository(options) {
|
|
|
11543
12282
|
pages.push(page);
|
|
11544
12283
|
if (isRestrictedPage) restrictedPages.add(page);
|
|
11545
12284
|
for (const assetPath of pageAssetReferences) addAssetReference(assetPath, file.relativePath);
|
|
11546
|
-
if (publishedText !== void 0) addPathReferences(publishedText, posix4.dirname(file.relativePath).replace(/^\.$/, ""), publishedExact, publishedLoose);
|
|
12285
|
+
if (publishedText !== void 0) addPathReferences(publishedText, posix4.dirname(file.relativePath).replace(/^\.$/, ""), publishedExact, publishedLoose, docusaurusPaths(posix4.join(docusaurusDocsPrefix, posix4.dirname(file.relativePath))));
|
|
11547
12286
|
if (hasDocusaurusDocCardList) {
|
|
11548
12287
|
docusaurusDocCardRoutes.add(page.navigationId);
|
|
11549
12288
|
}
|
|
@@ -11561,6 +12300,7 @@ function migrateRepository(options) {
|
|
|
11561
12300
|
}
|
|
11562
12301
|
}
|
|
11563
12302
|
if (docusaurusDescriptor) {
|
|
12303
|
+
if (docusaurusDescriptor.unlisted) page.hidden = true;
|
|
11564
12304
|
docusaurusDescriptors.push({ ...docusaurusDescriptor, title: page.title });
|
|
11565
12305
|
}
|
|
11566
12306
|
}
|
|
@@ -11579,8 +12319,8 @@ function migrateRepository(options) {
|
|
|
11579
12319
|
try {
|
|
11580
12320
|
const absolutePath = source ? resolveWithinRoot(fernProjectRoot, source.replace(/^\/+/, ""), repositoryDir) : resolveWithin(repositoryDir, candidate);
|
|
11581
12321
|
if (!existsSync5(absolutePath) || !lstatSync6(absolutePath).isFile()) return;
|
|
11582
|
-
const realRoot =
|
|
11583
|
-
const realPath =
|
|
12322
|
+
const realRoot = realpathSync4(repositoryDir);
|
|
12323
|
+
const realPath = realpathSync4(absolutePath);
|
|
11584
12324
|
const confined = relative6(realRoot, realPath);
|
|
11585
12325
|
if (confined === ".." || confined.startsWith(`..${sep3}`) || isAbsolute3(confined)) return;
|
|
11586
12326
|
const withinFern = relative6(fernProjectRoot, absolutePath);
|
|
@@ -11617,6 +12357,7 @@ function migrateRepository(options) {
|
|
|
11617
12357
|
}));
|
|
11618
12358
|
}) : [];
|
|
11619
12359
|
const assetCandidates = [];
|
|
12360
|
+
const seenAssetSources = /* @__PURE__ */ new Map();
|
|
11620
12361
|
const mintlifyFonts = platform === "mintlify" ? mintlifyFontSources(mintlifyConfig) : [];
|
|
11621
12362
|
const fontRelativePaths = new Set(mintlifyFonts.flatMap((font) => font.path ? [font.path] : []));
|
|
11622
12363
|
const importedComponentSources = new Set((componentMigrator?.files() ?? []).flatMap((file) => {
|
|
@@ -11663,6 +12404,11 @@ function migrateRepository(options) {
|
|
|
11663
12404
|
const isDocusaurusStatic = platform === "docusaurus" && firstSegment === "static";
|
|
11664
12405
|
const assetPath = normalizeAssetPath(firstSegment === "public" ? file.relativePath.slice("public/".length) : isDocusaurusStatic ? file.relativePath.slice("static/".length) : file.relativePath);
|
|
11665
12406
|
if (!assetPath) continue;
|
|
12407
|
+
const sourceKey = canonicalPath(file.absolutePath);
|
|
12408
|
+
const destinations = seenAssetSources.get(sourceKey) ?? /* @__PURE__ */ new Set();
|
|
12409
|
+
if (destinations.has(assetPath)) continue;
|
|
12410
|
+
destinations.add(assetPath);
|
|
12411
|
+
seenAssetSources.set(sourceKey, destinations);
|
|
11666
12412
|
if (siteKind) {
|
|
11667
12413
|
siteAssetPaths.set(assetPath, siteKind);
|
|
11668
12414
|
if (siteKind === "font") fontAssetByRelative.set(file.relativePath, assetPath);
|
|
@@ -11682,8 +12428,9 @@ function migrateRepository(options) {
|
|
|
11682
12428
|
const destinationCounts = /* @__PURE__ */ new Map();
|
|
11683
12429
|
for (const { assetPath } of assetCandidates) destinationCounts.set(assetPath, (destinationCounts.get(assetPath) ?? 0) + 1);
|
|
11684
12430
|
if (trackPublishedRefs) {
|
|
11685
|
-
const publishedConfig = JSON.stringify(mintlifyConfig ?? {}, (_key, value) => value && typeof value === "object" && !Array.isArray(value) && navigationGateReason(value) ? void 0 : value);
|
|
11686
|
-
addPathReferences(publishedConfig, "", publishedExact, publishedLoose);
|
|
12431
|
+
const publishedConfig = platform === "mintlify" ? JSON.stringify(mintlifyConfig ?? {}, (_key, value) => value && typeof value === "object" && !Array.isArray(value) && navigationGateReason(value) ? void 0 : value) : JSON.stringify([docsConfig, docusaurusProjectRoot ? readDocusaurusSiteSettings(docusaurusProjectRoot) : null]);
|
|
12432
|
+
addPathReferences(publishedConfig, "", publishedExact, publishedLoose, docusaurusPaths(""));
|
|
12433
|
+
if (fernRawConfig) addFernConfigPathReferences(fernRawConfig, publishedExact, publishedLoose);
|
|
11687
12434
|
}
|
|
11688
12435
|
for (const file of componentMigrator?.files() ?? []) {
|
|
11689
12436
|
if (!trackPublishedRefs || typeof file.content !== "string") continue;
|
|
@@ -11692,7 +12439,7 @@ function migrateRepository(options) {
|
|
|
11692
12439
|
}
|
|
11693
12440
|
const failClosedAssets = trackPublishedRefs;
|
|
11694
12441
|
const isPublicReachable = (assetPath) => referencedAssetPaths.has(assetPath) || publishedExact.has(assetPath);
|
|
11695
|
-
const isQuarantinedAsset = (assetPath) => (withheldAssetPaths.has(assetPath) || failClosedAssets && !siteAssetPaths.has(assetPath)) &&
|
|
12442
|
+
const isQuarantinedAsset = (assetPath) => (withheldAssetPaths.has(assetPath) || failClosedAssets && !siteAssetPaths.has(assetPath)) && !(isPublicReachable(assetPath) && !(failClosedAssets && (destinationCounts.get(assetPath) ?? 0) > 1));
|
|
11696
12443
|
let withheldAssetCount = 0;
|
|
11697
12444
|
let unreferencedAssetCount = 0;
|
|
11698
12445
|
let totalAssetBytes = 0;
|
|
@@ -11913,7 +12660,7 @@ function migrateRepository(options) {
|
|
|
11913
12660
|
}
|
|
11914
12661
|
if (docsConfig.tabs.length === 0) docsConfig = buildNavigationFromPages(pages);
|
|
11915
12662
|
if (platform === "fern" && fernProjectRoot) {
|
|
11916
|
-
const sections = fernApiSections.length > 0 ? fernApiSections : [{ nameExplicit: false }];
|
|
12663
|
+
const sections = fernApiSections.length > 0 ? fernApiSections : fernRestrictedApiSections.length > 0 ? [] : [{ nameExplicit: false }];
|
|
11917
12664
|
const resolvedSpecs = [];
|
|
11918
12665
|
const specFilenameSources = /* @__PURE__ */ new Map();
|
|
11919
12666
|
for (const section of sections) {
|
|
@@ -11927,7 +12674,8 @@ function migrateRepository(options) {
|
|
|
11927
12674
|
filename = `${prefix}${filename}`;
|
|
11928
12675
|
}
|
|
11929
12676
|
specFilenameSources.set(filename, spec.absolutePath);
|
|
11930
|
-
const specContent =
|
|
12677
|
+
const specContent = readSpecFile(spec.absolutePath, spec.relativePath, warnings);
|
|
12678
|
+
if (!specContent) continue;
|
|
11931
12679
|
if (!assets.some((asset) => asset.path === specAssetPath(filename))) {
|
|
11932
12680
|
assets.push(specAsset(filename, specContent));
|
|
11933
12681
|
}
|
|
@@ -11959,6 +12707,19 @@ function migrateRepository(options) {
|
|
|
11959
12707
|
message: checked.length ? `No OpenAPI/AsyncAPI spec could be found for the "${section.name}" API. Checked: ${checked.join(", ")}. Point generators.yml at a spec file that exists.` : `No OpenAPI/AsyncAPI spec could be found for the "${section.name}" API, because its api-name is not a valid folder name. Add the spec manually.`
|
|
11960
12708
|
});
|
|
11961
12709
|
}
|
|
12710
|
+
const publicSpecFiles = new Set(specFilenameSources.values());
|
|
12711
|
+
for (const section of fernRestrictedApiSections) {
|
|
12712
|
+
const spec = findFernConfiguredOpenApi(fernProjectRoot, repositoryDir, section.name, section.nameExplicit, warnings).spec ?? (!section.nameExplicit ? findOpenApi(files) : null);
|
|
12713
|
+
if (!spec || publicSpecFiles.has(spec.absolutePath)) continue;
|
|
12714
|
+
const content = readSpecFile(spec.absolutePath, spec.relativePath, warnings);
|
|
12715
|
+
if (!content) continue;
|
|
12716
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${specAssetPath(basename3(spec.relativePath))}`, content });
|
|
12717
|
+
warnings.push({
|
|
12718
|
+
code: "gated-page",
|
|
12719
|
+
message: `The "${section.name ?? "API"}" API reference is limited to signed-in roles in Fern, so it was NOT published. Its spec was saved at ${QUARANTINE_DIRECTORY}/assets/${specAssetPath(basename3(spec.relativePath))}.`,
|
|
12720
|
+
source: spec.relativePath
|
|
12721
|
+
});
|
|
12722
|
+
}
|
|
11962
12723
|
if (resolvedSpecs.length > 0) {
|
|
11963
12724
|
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs, warnings);
|
|
11964
12725
|
const { operationLinks, prefixLandings } = apiOperationLinkMap(
|
|
@@ -12030,14 +12791,15 @@ function migrateRepository(options) {
|
|
|
12030
12791
|
if (endpointRedirects.length > 0) docsConfig = { ...docsConfig, redirects: [...docsConfig.redirects ?? [], ...endpointRedirects] };
|
|
12031
12792
|
} else {
|
|
12032
12793
|
const fallback = findOpenApi(files);
|
|
12033
|
-
|
|
12794
|
+
const fallbackContent = fallback ? readSpecFile(fallback.absolutePath, fallback.relativePath, warnings) : void 0;
|
|
12795
|
+
if (fallback && fallbackContent) {
|
|
12034
12796
|
const filename = basename3(fallback.relativePath);
|
|
12035
12797
|
const matches = (ref) => specRefMatches(ref, fallback.relativePath, filename, true);
|
|
12036
12798
|
if (withheldSpecRefs.some(matches) && !publishedSpecRefs.some(matches)) {
|
|
12037
|
-
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${specAssetPath(filename)}`, content:
|
|
12799
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${specAssetPath(filename)}`, content: fallbackContent });
|
|
12038
12800
|
withheldAssetCount++;
|
|
12039
12801
|
} else {
|
|
12040
|
-
const pruned = excludeWithheldOperations({ sourcePath: fallback.relativePath, filename, content:
|
|
12802
|
+
const pruned = excludeWithheldOperations({ sourcePath: fallback.relativePath, filename, content: fallbackContent }, withheldSpecRefs, keptSpecRefs);
|
|
12041
12803
|
if (!assets.some((asset) => asset.path === specAssetPath(filename))) {
|
|
12042
12804
|
assets.push(specAsset(filename, pruned.content));
|
|
12043
12805
|
}
|
|
@@ -12312,7 +13074,14 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
12312
13074
|
}
|
|
12313
13075
|
const isTopLevelDocusaurus = platform === "docusaurus" && docusaurusProjectRoot && !options.docusaurusSkipRedirects;
|
|
12314
13076
|
const docusaurusSettings = isTopLevelDocusaurus ? readDocusaurusSiteSettings(docusaurusProjectRoot) : void 0;
|
|
13077
|
+
const presentation = docusaurusSettings ? readDocusaurusPresentation(docusaurusProjectRoot) : void 0;
|
|
12315
13078
|
if (docusaurusSettings) {
|
|
13079
|
+
if (presentation) {
|
|
13080
|
+
assets.push({ path: "_thally/source-theme.css", content: Buffer.from(presentation.css) });
|
|
13081
|
+
docsConfig = { ...docsConfig, stylesheets: [...docsConfig.stylesheets ?? [], "/_thally/source-theme.css"], ...presentation.headingFont ? { fonts: { ...docsConfig.fonts, heading: { family: presentation.headingFont, weight: ["400", "500", "600", "700"] } } } : {} };
|
|
13082
|
+
if (presentation.unresolved) warnings.push({ code: "unsupported-config", message: "Docusaurus Sass/Tailwind theme values could not be resolved statically; provide compiled build/assets/css/styles.*.css to retain those colors." });
|
|
13083
|
+
}
|
|
13084
|
+
if (docusaurusSettings.banner) docsConfig = { ...docsConfig, banner: docusaurusSettings.banner };
|
|
12316
13085
|
const importedRoutes = new Set(pages.map((page) => page.navigationId));
|
|
12317
13086
|
const sourceOrigin = readDocusaurusSiteOrigin(docusaurusProjectRoot);
|
|
12318
13087
|
const externalized = /* @__PURE__ */ new Set();
|
|
@@ -12383,7 +13152,7 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
12383
13152
|
if (!docsConfig.navigation?.display && docsConfig.tabs.filter((tab) => !tab.hidden).length > 6) {
|
|
12384
13153
|
docsConfig = { ...docsConfig, navigation: { display: "dropdown" } };
|
|
12385
13154
|
}
|
|
12386
|
-
const themeColors = mintlifyConfig ? mintlifyThemeColors(mintlifyConfig.colors) : fernRawConfig ? fernThemeColors(fernRawConfig.colors) : isTopLevelDocusaurus ? readDocusaurusThemeColor(docusaurusProjectRoot) : void 0;
|
|
13155
|
+
const themeColors = mintlifyConfig ? mintlifyThemeColors(mintlifyConfig.colors) : fernRawConfig ? fernThemeColors(fernRawConfig.colors) ?? (fernRawConfig["global-theme"] === "nvidia" ? { primary: "#76B900" } : void 0) : isTopLevelDocusaurus ? presentation?.colors ?? readDocusaurusThemeColor(docusaurusProjectRoot) : void 0;
|
|
12387
13156
|
const assetPaths = new Set(assets.map((asset) => asset.path));
|
|
12388
13157
|
const publicBrandPath = (value) => {
|
|
12389
13158
|
if (typeof value !== "string" || /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(value)) return void 0;
|
|
@@ -12490,7 +13259,12 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
12490
13259
|
message: (sawPublicTrue ? "Some pages set `public: true`, which means the source site used Mintlify authentication and every page WITHOUT it was private. " : "") + "Access control set in the Mintlify dashboard is not visible in the repository. Check the source site's dashboard access settings before publishing: until docs.json `auth` is configured, Thally publishes every imported page that has no `groups` or `public: false` to everyone. Confirm nothing here was meant to stay private before deploying."
|
|
12491
13260
|
});
|
|
12492
13261
|
}
|
|
12493
|
-
if (fernRawConfig?.
|
|
13262
|
+
if (typeof fernRawConfig?.["global-theme"] === "string") warnings.push({
|
|
13263
|
+
code: "unsupported-config",
|
|
13264
|
+
message: `Fern global theme ${JSON.stringify(fernRawConfig["global-theme"])} could not be fetched during local migration; local overrides and Thally default styling tokens were applied.`,
|
|
13265
|
+
source: "global-theme"
|
|
13266
|
+
});
|
|
13267
|
+
if (fernRawConfig?.logo && !fernRawConfig["global-theme"] && !logoLight && !logo.light) warnings.push({
|
|
12494
13268
|
code: "unsupported-config",
|
|
12495
13269
|
message: "Fern supplied a logo through its global theme without a local asset; add a logo path to docs.json after import."
|
|
12496
13270
|
});
|
|
@@ -12541,6 +13315,7 @@ function renderPage(bundle, page) {
|
|
|
12541
13315
|
return [
|
|
12542
13316
|
"---",
|
|
12543
13317
|
`title: ${yamlString(page.title)}`,
|
|
13318
|
+
page.headingTitle ? `headingTitle: ${yamlString(page.headingTitle)}` : null,
|
|
12544
13319
|
page.navTitle ? `navTitle: ${yamlString(page.navTitle)}` : null,
|
|
12545
13320
|
page.icon ? `icon: ${yamlString(page.icon)}` : null,
|
|
12546
13321
|
page.iconType ? `iconType: ${yamlString(page.iconType)}` : null,
|
|
@@ -12673,7 +13448,7 @@ import { lookup } from "dns/promises";
|
|
|
12673
13448
|
import { request } from "https";
|
|
12674
13449
|
import { isIP } from "net";
|
|
12675
13450
|
import { parse as parseYaml7 } from "yaml";
|
|
12676
|
-
var
|
|
13451
|
+
var MAX_SPEC_BYTES2 = 25e6;
|
|
12677
13452
|
var MAX_REDIRECTS = 3;
|
|
12678
13453
|
function isPublicAddress(address, family) {
|
|
12679
13454
|
if (family === 4) {
|
|
@@ -12724,7 +13499,7 @@ async function requestPinnedSpec(url) {
|
|
|
12724
13499
|
return;
|
|
12725
13500
|
}
|
|
12726
13501
|
const statedBytes = Number(response.headers["content-length"] ?? 0);
|
|
12727
|
-
if (statedBytes >
|
|
13502
|
+
if (statedBytes > MAX_SPEC_BYTES2) {
|
|
12728
13503
|
response.destroy();
|
|
12729
13504
|
reject(new Error("Remote OpenAPI spec exceeds the 25 MB import limit."));
|
|
12730
13505
|
return;
|
|
@@ -12733,7 +13508,7 @@ async function requestPinnedSpec(url) {
|
|
|
12733
13508
|
let bytes = 0;
|
|
12734
13509
|
response.on("data", (chunk) => {
|
|
12735
13510
|
bytes += chunk.length;
|
|
12736
|
-
if (bytes >
|
|
13511
|
+
if (bytes > MAX_SPEC_BYTES2) {
|
|
12737
13512
|
response.destroy(new Error("Remote OpenAPI spec exceeds the 25 MB import limit."));
|
|
12738
13513
|
return;
|
|
12739
13514
|
}
|
|
@@ -12752,7 +13527,7 @@ async function downloadSpec(value, fetcher) {
|
|
|
12752
13527
|
const response = await fetcher(url, { accept: "application/json, application/yaml, text/yaml" });
|
|
12753
13528
|
validatedSpecUrl(response.finalUrl.toString());
|
|
12754
13529
|
const body = Buffer.from(response.body);
|
|
12755
|
-
if (body.length >
|
|
13530
|
+
if (body.length > MAX_SPEC_BYTES2) throw new Error("Remote OpenAPI spec exceeds the 25 MB import limit.");
|
|
12756
13531
|
return body;
|
|
12757
13532
|
}
|
|
12758
13533
|
for (let hop = 0; hop <= MAX_REDIRECTS; hop++) {
|