@thallylabs/migrate 0.2.7 → 0.2.9
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 +37 -5
- package/dist/index.d.ts +31 -1
- package/dist/index.js +1599 -226
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
import { compileSync as compileSync2 } from "@mdx-js/mdx";
|
|
3
3
|
import { spawn } from "child_process";
|
|
4
4
|
import {
|
|
5
|
+
closeSync,
|
|
5
6
|
existsSync as existsSync5,
|
|
6
7
|
lstatSync as lstatSync5,
|
|
8
|
+
openSync,
|
|
7
9
|
readFileSync as readFileSync5,
|
|
8
|
-
|
|
10
|
+
readSync,
|
|
11
|
+
readdirSync as readdirSync2,
|
|
9
12
|
realpathSync as realpathSync2,
|
|
10
13
|
rmSync,
|
|
11
14
|
statSync
|
|
12
15
|
} from "fs";
|
|
13
16
|
import { createRequire } from "module";
|
|
14
|
-
import { basename as
|
|
17
|
+
import { basename as basename3, dirname as dirname4, extname as extname5, isAbsolute as isAbsolute2, relative as relative5, resolve as resolvePath, sep as sep2 } from "path";
|
|
15
18
|
import { parse as parseYaml4 } from "yaml";
|
|
19
|
+
import * as ts2 from "typescript";
|
|
16
20
|
|
|
17
21
|
// src/components.ts
|
|
18
22
|
import { createHash } from "crypto";
|
|
@@ -50,6 +54,8 @@ function parseFrontmatter(raw) {
|
|
|
50
54
|
|
|
51
55
|
// src/mdx.ts
|
|
52
56
|
import { compileSync } from "@mdx-js/mdx";
|
|
57
|
+
import { nameToEmoji } from "gemoji";
|
|
58
|
+
import remarkGfm from "remark-gfm";
|
|
53
59
|
import remarkMdx from "remark-mdx";
|
|
54
60
|
import remarkParse from "remark-parse";
|
|
55
61
|
import { unified } from "unified";
|
|
@@ -125,7 +131,7 @@ function titleFromId(id) {
|
|
|
125
131
|
if (value === "introduction") return "Introduction";
|
|
126
132
|
return value.split(/[-_]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
127
133
|
}
|
|
128
|
-
var descriptionParser = unified().use(remarkParse).use(remarkMdx);
|
|
134
|
+
var descriptionParser = unified().use(remarkParse).use(remarkGfm).use(remarkMdx);
|
|
129
135
|
function firstParagraph(content) {
|
|
130
136
|
function plainText(node) {
|
|
131
137
|
if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
|
|
@@ -159,19 +165,21 @@ function docusaurusAdmonitionTag(kind) {
|
|
|
159
165
|
function normalizeDocusaurusAdmonitions(body) {
|
|
160
166
|
const lines = body.split("\n");
|
|
161
167
|
const openAdmonitions = [];
|
|
168
|
+
const headingLines = [];
|
|
162
169
|
let codeFence = null;
|
|
163
|
-
|
|
164
|
-
const codeMatch = line.match(/^\s
|
|
170
|
+
const output = lines.map((line, index) => {
|
|
171
|
+
const codeMatch = line.match(/^\s{0,3}(`{3,}|~{3,})/);
|
|
165
172
|
if (codeMatch) {
|
|
166
|
-
if (!codeFence) codeFence = codeMatch[1]
|
|
167
|
-
else if (codeMatch[1][0] === codeFence) codeFence = null;
|
|
173
|
+
if (!codeFence) codeFence = codeMatch[1];
|
|
174
|
+
else if (codeMatch[1][0] === codeFence[0] && codeMatch[1].length >= codeFence.length && /^\s*$/.test(line.slice(codeMatch[0].length))) codeFence = null;
|
|
168
175
|
return line;
|
|
169
176
|
}
|
|
170
177
|
if (codeFence) return line;
|
|
178
|
+
if (/^#{1,6}\s/.test(line)) headingLines.push(index);
|
|
171
179
|
const opening = line.match(/^\s*(:{3,})(note|tip|info|warning|caution|danger)(?:\[([^\]]+)\]|\s+(.+))?\s*$/i);
|
|
172
180
|
if (opening) {
|
|
173
181
|
const tag = docusaurusAdmonitionTag(opening[2].toLowerCase());
|
|
174
|
-
openAdmonitions.push({ delimiter: opening[1], tag });
|
|
182
|
+
openAdmonitions.push({ delimiter: opening[1], tag, openedAt: index });
|
|
175
183
|
const title = (opening[3] ?? opening[4])?.trim();
|
|
176
184
|
return title ? `<${tag}>
|
|
177
185
|
**${title}**` : `<${tag}>`;
|
|
@@ -183,7 +191,74 @@ function normalizeDocusaurusAdmonitions(body) {
|
|
|
183
191
|
return `</${current.tag}>`;
|
|
184
192
|
}
|
|
185
193
|
return line;
|
|
186
|
-
})
|
|
194
|
+
});
|
|
195
|
+
const closingByLine = /* @__PURE__ */ new Map();
|
|
196
|
+
for (const open of openAdmonitions.reverse()) {
|
|
197
|
+
const at = headingLines.find((line) => line > open.openedAt) ?? output.length;
|
|
198
|
+
closingByLine.set(at, [...closingByLine.get(at) ?? [], `</${open.tag}>`]);
|
|
199
|
+
}
|
|
200
|
+
for (const [at, closing] of [...closingByLine].sort(([left], [right]) => right - left)) output.splice(at, 0, ...closing);
|
|
201
|
+
return output.join("\n");
|
|
202
|
+
}
|
|
203
|
+
function unwrapDocusaurusMdxCodeBlocks(body) {
|
|
204
|
+
const output = [];
|
|
205
|
+
let fence;
|
|
206
|
+
let isMdxCodeBlock = false;
|
|
207
|
+
for (const line of body.split("\n")) {
|
|
208
|
+
const opening = !fence && line.match(/^ {0,3}(`{3,}|~{3,})mdx-code-block\s*$/);
|
|
209
|
+
if (opening) {
|
|
210
|
+
fence = opening[1];
|
|
211
|
+
isMdxCodeBlock = true;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (fence) {
|
|
215
|
+
const closing = line.match(/^ {0,3}(`{3,}|~{3,})\s*$/);
|
|
216
|
+
if (closing && closing[1][0] === fence[0] && closing[1].length >= fence.length) {
|
|
217
|
+
if (!isMdxCodeBlock) output.push(line);
|
|
218
|
+
fence = void 0;
|
|
219
|
+
isMdxCodeBlock = false;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (isMdxCodeBlock && /^\s*(?:import|export)\b/.test(line)) return body;
|
|
223
|
+
output.push(line);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const ordinaryFence = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
|
227
|
+
if (ordinaryFence) {
|
|
228
|
+
fence = ordinaryFence[1];
|
|
229
|
+
isMdxCodeBlock = false;
|
|
230
|
+
}
|
|
231
|
+
output.push(line);
|
|
232
|
+
}
|
|
233
|
+
return isMdxCodeBlock ? body : output.join("\n");
|
|
234
|
+
}
|
|
235
|
+
function normalizeExplicitHeadingIds(body) {
|
|
236
|
+
return replaceOutsideCode(body, (segment) => segment.split("\n").map((line) => {
|
|
237
|
+
if (!/^[ \t]{0,3}#{1,6}[ \t]+/.test(line)) return line;
|
|
238
|
+
const trimmed = line.trimEnd();
|
|
239
|
+
if (trimmed.endsWith("}")) {
|
|
240
|
+
const marker = trimmed.lastIndexOf(" {#");
|
|
241
|
+
if (marker >= 0) {
|
|
242
|
+
const id = trimmed.slice(marker + 3, -1);
|
|
243
|
+
if (/^[A-Za-z][A-Za-z0-9_-]*$/.test(id)) {
|
|
244
|
+
return `<a id="${id}"></a>
|
|
245
|
+
${trimmed.slice(0, marker).trimEnd()}`;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
if (trimmed.endsWith("*/}")) {
|
|
250
|
+
const marker = trimmed.lastIndexOf(" {/*");
|
|
251
|
+
if (marker >= 0) {
|
|
252
|
+
const comment = trimmed.slice(marker + 4, -3).trim();
|
|
253
|
+
const id = comment.startsWith("#") ? comment.slice(1) : "";
|
|
254
|
+
if (/^[A-Za-z][A-Za-z0-9_.:-]*$/.test(id)) {
|
|
255
|
+
return `<a id="${id}"></a>
|
|
256
|
+
${trimmed.slice(0, marker).trimEnd()}`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return line;
|
|
261
|
+
}).join("\n"));
|
|
187
262
|
}
|
|
188
263
|
var GLOBAL_DOCUSARUS_COMPONENTS = /* @__PURE__ */ new Set([
|
|
189
264
|
"Tabs",
|
|
@@ -192,19 +267,11 @@ var GLOBAL_DOCUSARUS_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
192
267
|
"DocCardList",
|
|
193
268
|
"TOCInline"
|
|
194
269
|
]);
|
|
195
|
-
function
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
const component = trimmed.slice("import ".length, separator).trim();
|
|
201
|
-
if (!GLOBAL_DOCUSARUS_COMPONENTS.has(component)) return false;
|
|
202
|
-
const source = trimmed.slice(separator + " from ".length);
|
|
203
|
-
if (source.length < 3) return false;
|
|
204
|
-
const quote = source[0];
|
|
205
|
-
if (quote !== "'" && quote !== '"' || source.at(-1) !== quote) return false;
|
|
206
|
-
const moduleName = source.slice(1, -1);
|
|
207
|
-
return moduleName.startsWith("@theme/") || moduleName.startsWith("@docusaurus/");
|
|
270
|
+
function removeGlobalDocusaurusImports(body) {
|
|
271
|
+
return replaceOutsideCode(body, (segment) => segment.replace(
|
|
272
|
+
/\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])(@(?:theme|docusaurus)\/[^'"]+)\2\s*;?/g,
|
|
273
|
+
(statement, component) => GLOBAL_DOCUSARUS_COMPONENTS.has(component) ? "" : statement
|
|
274
|
+
));
|
|
208
275
|
}
|
|
209
276
|
var JS_LITERAL_KEYWORDS = /* @__PURE__ */ new Set(["true", "false", "null", "undefined"]);
|
|
210
277
|
var BARE_IDENTIFIER_PATH = /^[\p{ID_Start}$_][\p{ID_Continue}$]*(?:\.[\p{ID_Start}$_][\p{ID_Continue}$]*)*$/u;
|
|
@@ -221,6 +288,20 @@ var SAFE_BUILTIN_ROOTS = /* @__PURE__ */ new Set([
|
|
|
221
288
|
"BigInt",
|
|
222
289
|
"Reflect"
|
|
223
290
|
]);
|
|
291
|
+
function leadingPageHeading(body) {
|
|
292
|
+
try {
|
|
293
|
+
const root = descriptionParser.parse(body);
|
|
294
|
+
const first = root.children?.find((node) => node.type !== "mdxjsEsm" && node.type !== "html");
|
|
295
|
+
if (first?.type !== "heading" || first.depth !== 1) return void 0;
|
|
296
|
+
const text = (node) => node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" ? "" : node.value ?? (node.children ?? []).map(text).join("");
|
|
297
|
+
const title = text(first).replace(/\s+/g, " ").trim();
|
|
298
|
+
const start = first.position?.start.offset;
|
|
299
|
+
const end = first.position?.end.offset;
|
|
300
|
+
return title && start !== void 0 && end !== void 0 ? { title, start, end } : void 0;
|
|
301
|
+
} catch {
|
|
302
|
+
return void 0;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
224
305
|
function collectPatternNames(pattern, names) {
|
|
225
306
|
switch (pattern.type) {
|
|
226
307
|
case "Identifier":
|
|
@@ -418,7 +499,11 @@ function escapeFernLiteralBraces(raw) {
|
|
|
418
499
|
const end = node.position?.end.offset;
|
|
419
500
|
const root2 = value.split(".")[0];
|
|
420
501
|
const safeBuiltinPath = value.includes(".") && SAFE_BUILTIN_ROOTS.has(root2);
|
|
421
|
-
|
|
502
|
+
const isTemplatePlaceholder = /^\{\s*[A-Za-z_$][\w$.-]*\s*\}$/.test(value);
|
|
503
|
+
const isIssueList = /^[A-Za-z][A-Za-z0-9]*-\d+(?:\s*,\s*[A-Za-z][A-Za-z0-9]*-\d+)*$/.test(value);
|
|
504
|
+
if ((isTemplatePlaceholder || isIssueList) && start !== void 0 && end !== void 0) {
|
|
505
|
+
edits.push({ start, end, value: body.slice(start, end).replace(/\\/g, "\\\\").replace(/[{}]/g, "\\$&") });
|
|
506
|
+
} else if (BARE_IDENTIFIER_PATH.test(value) && !JS_LITERAL_KEYWORDS.has(value) && !declared.has(root2) && !safeBuiltinPath && start !== void 0 && end !== void 0) {
|
|
422
507
|
edits.push({ start, end, value: `\\{${value}\\}` });
|
|
423
508
|
}
|
|
424
509
|
return;
|
|
@@ -532,10 +617,10 @@ function maskCode(body) {
|
|
|
532
617
|
let codeFence = null;
|
|
533
618
|
let fenceBuffer = [];
|
|
534
619
|
for (const line of source.split("\n")) {
|
|
535
|
-
const fenceMatch = line.match(/^\s
|
|
620
|
+
const fenceMatch = line.match(/^\s{0,3}(`{3,}|~{3,})/);
|
|
536
621
|
if (codeFence) {
|
|
537
622
|
fenceBuffer.push(line);
|
|
538
|
-
if (fenceMatch && fenceMatch[1][0] === codeFence) {
|
|
623
|
+
if (fenceMatch && fenceMatch[1][0] === codeFence[0] && fenceMatch[1].length >= codeFence.length && /^\s*$/.test(line.slice(fenceMatch[0].length))) {
|
|
539
624
|
lines.push(stash(fenceBuffer.join("\n")));
|
|
540
625
|
codeFence = null;
|
|
541
626
|
fenceBuffer = [];
|
|
@@ -543,7 +628,7 @@ function maskCode(body) {
|
|
|
543
628
|
continue;
|
|
544
629
|
}
|
|
545
630
|
if (fenceMatch) {
|
|
546
|
-
codeFence = fenceMatch[1]
|
|
631
|
+
codeFence = fenceMatch[1];
|
|
547
632
|
fenceBuffer = [line];
|
|
548
633
|
continue;
|
|
549
634
|
}
|
|
@@ -565,14 +650,104 @@ function normalizeHtmlComments(body) {
|
|
|
565
650
|
function normalizeFernCallouts(body) {
|
|
566
651
|
return replaceOutsideCode(body, (whole) => {
|
|
567
652
|
const openTags = [];
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
653
|
+
const output = [];
|
|
654
|
+
let cursor = 0;
|
|
655
|
+
while (cursor < whole.length) {
|
|
656
|
+
const opening = whole.indexOf("<Callout", cursor);
|
|
657
|
+
const closing = whole.indexOf("</Callout>", cursor);
|
|
658
|
+
const next = opening < 0 ? closing : closing < 0 ? opening : Math.min(opening, closing);
|
|
659
|
+
if (next < 0) break;
|
|
660
|
+
output.push(whole.slice(cursor, next));
|
|
661
|
+
if (next === closing) {
|
|
662
|
+
const tag = openTags.pop();
|
|
663
|
+
output.push(tag ? `</${tag}>` : "</Callout>");
|
|
664
|
+
cursor = next + "</Callout>".length;
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
const attributeStart = next + "<Callout".length;
|
|
668
|
+
if (!/[\s/>]/.test(whole[attributeStart] ?? "")) {
|
|
669
|
+
output.push("<Callout");
|
|
670
|
+
cursor = attributeStart;
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
let end = attributeStart;
|
|
674
|
+
let quote = null;
|
|
675
|
+
let expressionDepth = 0;
|
|
676
|
+
for (; end < whole.length; end++) {
|
|
677
|
+
const character = whole[end];
|
|
678
|
+
if (quote) {
|
|
679
|
+
if (character === quote && whole[end - 1] !== "\\") quote = null;
|
|
680
|
+
} else if (character === '"' || character === "'") {
|
|
681
|
+
quote = character;
|
|
682
|
+
} else if (character === "{") {
|
|
683
|
+
expressionDepth++;
|
|
684
|
+
} else if (character === "}" && expressionDepth > 0) {
|
|
685
|
+
expressionDepth--;
|
|
686
|
+
} else if (character === ">" && expressionDepth === 0) {
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (end === whole.length) {
|
|
691
|
+
output.push(whole.slice(next));
|
|
692
|
+
cursor = whole.length;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
const attributes = whole.slice(attributeStart, end);
|
|
696
|
+
const intent = findStaticCalloutIntent(attributes);
|
|
697
|
+
const isSelfClosing = attributes.trimEnd().endsWith("/");
|
|
698
|
+
if (!intent) {
|
|
699
|
+
output.push(whole.slice(next, end + 1));
|
|
700
|
+
if (!isSelfClosing) openTags.push(null);
|
|
701
|
+
} else {
|
|
702
|
+
const value = intent.value.toLowerCase();
|
|
703
|
+
const tag = value === "warning" ? "Warning" : value === "success" || value === "tip" ? "Tip" : value === "error" || value === "danger" ? "Error" : "Note";
|
|
704
|
+
output.push(`<${tag}${attributes.slice(0, intent.start)}${attributes.slice(intent.end)}>`);
|
|
705
|
+
if (!isSelfClosing) openTags.push(tag);
|
|
706
|
+
}
|
|
707
|
+
cursor = end + 1;
|
|
708
|
+
}
|
|
709
|
+
output.push(whole.slice(cursor));
|
|
710
|
+
return output.join("");
|
|
574
711
|
});
|
|
575
712
|
}
|
|
713
|
+
function findStaticCalloutIntent(attributes) {
|
|
714
|
+
let cursor = 0;
|
|
715
|
+
while (cursor < attributes.length) {
|
|
716
|
+
const start = cursor;
|
|
717
|
+
while (/\s/.test(attributes[cursor] ?? "")) cursor++;
|
|
718
|
+
if (cursor === start) {
|
|
719
|
+
cursor++;
|
|
720
|
+
continue;
|
|
721
|
+
}
|
|
722
|
+
const nameStart = cursor;
|
|
723
|
+
while (/[A-Za-z0-9_-]/.test(attributes[cursor] ?? "")) cursor++;
|
|
724
|
+
const name = attributes.slice(nameStart, cursor);
|
|
725
|
+
while (/\s/.test(attributes[cursor] ?? "")) cursor++;
|
|
726
|
+
if (attributes[cursor] !== "=") continue;
|
|
727
|
+
cursor++;
|
|
728
|
+
while (/\s/.test(attributes[cursor] ?? "")) cursor++;
|
|
729
|
+
const quote = attributes[cursor];
|
|
730
|
+
if (quote !== '"' && quote !== "'") {
|
|
731
|
+
if (quote === "{") {
|
|
732
|
+
let depth = 0;
|
|
733
|
+
do {
|
|
734
|
+
if (attributes[cursor] === "{") depth++;
|
|
735
|
+
if (attributes[cursor] === "}") depth--;
|
|
736
|
+
cursor++;
|
|
737
|
+
} while (cursor < attributes.length && depth > 0);
|
|
738
|
+
} else {
|
|
739
|
+
while (cursor < attributes.length && !/\s/.test(attributes[cursor])) cursor++;
|
|
740
|
+
}
|
|
741
|
+
continue;
|
|
742
|
+
}
|
|
743
|
+
const valueStart = ++cursor;
|
|
744
|
+
while (cursor < attributes.length && attributes[cursor] !== quote) cursor++;
|
|
745
|
+
const value = attributes.slice(valueStart, cursor);
|
|
746
|
+
if (cursor < attributes.length) cursor++;
|
|
747
|
+
if (name === "intent") return { start, end: cursor, value };
|
|
748
|
+
}
|
|
749
|
+
return null;
|
|
750
|
+
}
|
|
576
751
|
function unwrapFernCodeBlockTabs(body) {
|
|
577
752
|
return replaceOutsideCode(body, (whole) => {
|
|
578
753
|
let groupDepth = 0;
|
|
@@ -679,25 +854,31 @@ ${result}`;
|
|
|
679
854
|
return result;
|
|
680
855
|
}
|
|
681
856
|
function normalizeDocusaurusTabs(body) {
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
const
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
const
|
|
695
|
-
const
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
857
|
+
let result = body;
|
|
858
|
+
for (let depth = 0; depth < 20; depth += 1) {
|
|
859
|
+
const updated = result.replace(/<Tabs\b([^>]*)>((?:(?!<Tabs\b)[\s\S])*?)<\/Tabs>/g, (_match, attributes, inner) => {
|
|
860
|
+
const labelByValue = /* @__PURE__ */ new Map();
|
|
861
|
+
const valuesSource = attributes.match(/\bvalues=\{(\[[\s\S]*?\])\s*\}/)?.[1];
|
|
862
|
+
if (valuesSource) {
|
|
863
|
+
for (const entry of valuesSource.matchAll(/\{([^{}]*)\}/g)) {
|
|
864
|
+
const label = entry[1].match(/\blabel:\s*(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean);
|
|
865
|
+
const value = entry[1].match(/\bvalue:\s*(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean);
|
|
866
|
+
if (value) labelByValue.set(value, label ?? value);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
const cleanedAttributes = attributes.replace(/\s*defaultValue=(?:\{[^}]*\}|"[^"]*"|'[^']*')/g, "").replace(/\s*values=\{[\s\S]*?\]\s*\}/g, "").replace(/\s*values=\{[^{}]*\}/g, "").replace(/\s*groupId=(?:"[^"]*"|'[^']*')/g, "").replace(/\s*queryString(?:=(?:\{[^}]*\}|"[^"]*"|'[^']*'))?/g, "");
|
|
870
|
+
const rewrittenInner = inner.replace(/<TabItem\b([^>]*)>/g, (_itemMatch, itemAttributes) => {
|
|
871
|
+
const label = itemAttributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean);
|
|
872
|
+
const value = itemAttributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean);
|
|
873
|
+
const title = label ?? (value && labelByValue.get(value)) ?? value ?? "Tab";
|
|
874
|
+
return `<Tab title="${title.replace(/"/g, """)}">`;
|
|
875
|
+
}).replace(/<\/TabItem>/g, "</Tab>");
|
|
876
|
+
return `<ThallyTabs${cleanedAttributes}>${rewrittenInner}</ThallyTabs>`;
|
|
877
|
+
});
|
|
878
|
+
if (updated === result) break;
|
|
879
|
+
result = updated;
|
|
880
|
+
}
|
|
881
|
+
return result.replace(/<ThallyTabs\b/g, "<Tabs").replace(/<\/ThallyTabs>/g, "</Tabs>");
|
|
701
882
|
}
|
|
702
883
|
var TAB_FENCE_OPEN = /^(`{3,}|~{3,})(\S*)\s+tab\b(.*)$/;
|
|
703
884
|
function normalizeDocusaurusTabBlocks(body) {
|
|
@@ -783,15 +964,72 @@ function convertHtmlStyleAttributes(body) {
|
|
|
783
964
|
(_match, tag, before, _quote, styleText, after, selfClose) => `<${tag}${before} style={${cssTextToStyleObjectLiteral(styleText)}}${after}${selfClose ? " /" : ""}>`
|
|
784
965
|
);
|
|
785
966
|
}
|
|
786
|
-
function
|
|
967
|
+
function normalizeMintlifyUpdateLabels(body) {
|
|
968
|
+
return body.replace(/(<Update\b[^>]*\blabel=)\{(<\>[\s\S]*?<\/\>)\}/g, (match, prefix, fragment) => {
|
|
969
|
+
let root;
|
|
970
|
+
try {
|
|
971
|
+
root = descriptionParser.parse(fragment);
|
|
972
|
+
} catch {
|
|
973
|
+
return match;
|
|
974
|
+
}
|
|
975
|
+
const plainText = (node) => {
|
|
976
|
+
if (node.type === "text" || node.type === "inlineCode") return node.value ?? "";
|
|
977
|
+
if (node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" || node.type === "mdxjsEsm") return null;
|
|
978
|
+
const parts = (node.children ?? []).map(plainText);
|
|
979
|
+
return parts.some((part) => part === null) ? null : parts.join("");
|
|
980
|
+
};
|
|
981
|
+
const label = plainText(root)?.replace(/\s+/g, " ").trim();
|
|
982
|
+
return label && !/[{}]/.test(label) ? `${prefix}{${JSON.stringify(label)}}` : match;
|
|
983
|
+
});
|
|
984
|
+
}
|
|
985
|
+
function normalizeMintlifyParameterAnchors(body) {
|
|
986
|
+
const seen = /* @__PURE__ */ new Set();
|
|
987
|
+
const anchorFor = (name) => {
|
|
988
|
+
const id = `param-${name.toLowerCase().replace(/[._]/g, "-")}`;
|
|
989
|
+
if (seen.has(id)) return "";
|
|
990
|
+
seen.add(id);
|
|
991
|
+
return `<a id="${id}"></a>
|
|
992
|
+
`;
|
|
993
|
+
};
|
|
994
|
+
let fence;
|
|
995
|
+
const withHeadings = body.split("\n").map((line) => {
|
|
996
|
+
const marker = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
|
997
|
+
if (marker) {
|
|
998
|
+
if (!fence) fence = marker[1];
|
|
999
|
+
else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && /^\s*$/.test(line.slice(marker[0].length))) fence = void 0;
|
|
1000
|
+
return line;
|
|
1001
|
+
}
|
|
1002
|
+
if (fence) return line;
|
|
1003
|
+
const heading = line.match(/^ {0,3}#{1,6}\s+`([A-Za-z][A-Za-z0-9._-]*)`(?:\s|$)/);
|
|
1004
|
+
return heading ? `${anchorFor(heading[1])}${line}` : line;
|
|
1005
|
+
}).join("\n");
|
|
1006
|
+
return replaceOutsideCode(withHeadings, (segment) => segment.replace(
|
|
1007
|
+
/<(ResponseField|ParamField)\b([^>]*?)\bname=(?:"([A-Za-z][A-Za-z0-9._-]*)"|'([A-Za-z][A-Za-z0-9._-]*)')([^>]*)>/g,
|
|
1008
|
+
(match, _tag, _before, doubleQuoted, singleQuoted) => `${anchorFor(doubleQuoted ?? singleQuoted)}${match}`
|
|
1009
|
+
));
|
|
1010
|
+
}
|
|
1011
|
+
function normalizeMdx(body, platform, unwrapMdxCodeBlocks = true) {
|
|
787
1012
|
const runFern = platform === "fern";
|
|
788
1013
|
const runMintlify = platform === "mintlify";
|
|
789
1014
|
const runDocusaurus = platform === "docusaurus";
|
|
790
|
-
|
|
1015
|
+
const sourceBody = runDocusaurus && unwrapMdxCodeBlocks ? unwrapDocusaurusMdxCodeBlocks(body) : body;
|
|
1016
|
+
let rewritten = normalizeDocusaurusAdmonitions(normalizeExplicitHeadingIds(runFern ? normalizeFernCallouts(sourceBody) : sourceBody));
|
|
791
1017
|
if (runDocusaurus) {
|
|
792
|
-
|
|
793
|
-
|
|
1018
|
+
rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
|
|
1019
|
+
/:([+\w-]+):/g,
|
|
1020
|
+
(shortcode, name) => nameToEmoji[name] ?? shortcode
|
|
1021
|
+
));
|
|
1022
|
+
rewritten = normalizeDocusaurusTabBlocks(normalizeDocusaurusTabs(removeGlobalDocusaurusImports(rewritten)));
|
|
1023
|
+
rewritten = replaceOutsideCode(rewritten, (segment) => segment.replace(
|
|
1024
|
+
/\bimport\s+([A-Za-z_$][\w$]*)\s+from\s+(['"])(@site\/[^'"]+)\2\s*;?/g,
|
|
1025
|
+
(statement, binding, _quote, _source, offset) => {
|
|
1026
|
+
const withoutStatement = segment.slice(0, offset) + segment.slice(offset + statement.length);
|
|
1027
|
+
return new RegExp(`\\b${binding}\\s*(?:\\.|\\[|\\})`).test(withoutStatement) ? statement : "";
|
|
1028
|
+
}
|
|
1029
|
+
));
|
|
1030
|
+
rewritten = rewritten.replace(/^\n{2,}/, "\n");
|
|
794
1031
|
}
|
|
1032
|
+
if (runMintlify) rewritten = normalizeMintlifyParameterAnchors(rewritten);
|
|
795
1033
|
rewritten = replaceOutsideCode(rewritten, (segment) => {
|
|
796
1034
|
let result = convertHtmlStyleAttributes(segment).replace(/<!--([\s\S]*?)-->/g, (_match, content) => `{/*${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>");
|
|
797
1035
|
if (runDocusaurus) {
|
|
@@ -801,7 +1039,7 @@ function normalizeMdx(body, platform) {
|
|
|
801
1039
|
}).replace(/<\/TabItem>/g, "</Tab>").replace(/<Link\b([^>]*)\bto=(?:"([^"]*)"|'([^']*)')([^>]*)>/g, (_match, before, doubleQuoted, singleQuoted, after) => `<a${before}href="${doubleQuoted ?? singleQuoted}"${after}>`).replace(/<\/Link>/g, "</a>").replace(/<(?:DocCardList|TOCInline)\b[^>]*\/>/g, "");
|
|
802
1040
|
}
|
|
803
1041
|
if (runMintlify) {
|
|
804
|
-
result = 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>");
|
|
1042
|
+
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>");
|
|
805
1043
|
}
|
|
806
1044
|
if (runFern) {
|
|
807
1045
|
result = result.replace(/<CodeBlocks(\s[^>]*)?>/g, "<CodeGroup$1>").replace(/<\/CodeBlocks>/g, "</CodeGroup>").replace(/<ParameterField\b/g, "<ParamField").replace(/<\/ParameterField>/g, "</ParamField>").replace(/<Cards(\s[^>]*)?>/g, "<CardGroup$1>").replace(/<\/Cards>/g, "</CardGroup>").replace(/<Success(\s[^>]*)?>/g, "<Tip$1>").replace(/<\/Success>/g, "</Tip>").replace(/<Launch(\s[^>]*)?>/g, "<Note$1>").replace(/<\/Launch>/g, "</Note>").replace(/<Files(\s[^>]*)?>\n?/g, "").replace(/<\/Files>/g, "");
|
|
@@ -809,7 +1047,15 @@ function normalizeMdx(body, platform) {
|
|
|
809
1047
|
return result;
|
|
810
1048
|
});
|
|
811
1049
|
if (runFern) rewritten = unwrapFernCodeBlockTabs(rewritten);
|
|
812
|
-
|
|
1050
|
+
const normalized = provideScopedGlobalReferences(rewritten);
|
|
1051
|
+
if (runDocusaurus && sourceBody !== body) {
|
|
1052
|
+
try {
|
|
1053
|
+
compileSync(normalized, { outputFormat: "program" });
|
|
1054
|
+
} catch {
|
|
1055
|
+
return normalizeMdx(body, platform, false);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
return normalized;
|
|
813
1059
|
}
|
|
814
1060
|
function parseMarkdownPage(input) {
|
|
815
1061
|
const parsed = parseFrontmatter(input.raw);
|
|
@@ -819,9 +1065,13 @@ function parseMarkdownPage(input) {
|
|
|
819
1065
|
...input.locale ? { locale: input.locale } : {}
|
|
820
1066
|
};
|
|
821
1067
|
const identity = input.resolveIdentity?.(parsed.data, fallbackIdentity) ?? fallbackIdentity;
|
|
822
|
-
|
|
1068
|
+
let body = normalizeMdx(parsed.content, input.platform).trim();
|
|
823
1069
|
const keywords = Array.isArray(parsed.data.keywords) ? parsed.data.keywords.filter((value) => typeof value === "string") : [];
|
|
824
|
-
const
|
|
1070
|
+
const heading = leadingPageHeading(body);
|
|
1071
|
+
const title = typeof parsed.data.title === "string" && parsed.data.title.trim() ? parsed.data.title.trim() : heading?.title ?? titleFromId(identity.navigationId);
|
|
1072
|
+
if (heading && heading.title === title) {
|
|
1073
|
+
body = `${body.slice(0, heading.start)}${body.slice(heading.end)}`.trim();
|
|
1074
|
+
}
|
|
825
1075
|
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;
|
|
826
1076
|
const badge = typeof parsed.data.tag === "string" && parsed.data.tag.trim() ? parsed.data.tag.trim() : typeof parsed.data.badge === "string" && parsed.data.badge.trim() ? parsed.data.badge.trim() : void 0;
|
|
827
1077
|
const sourceMode = typeof parsed.data.mode === "string" ? parsed.data.mode : void 0;
|
|
@@ -834,6 +1084,7 @@ function parseMarkdownPage(input) {
|
|
|
834
1084
|
title,
|
|
835
1085
|
navTitle,
|
|
836
1086
|
description,
|
|
1087
|
+
...input.platform === "docusaurus" || input.platform === "fern" ? { descriptionPlacement: "body" } : {},
|
|
837
1088
|
badge,
|
|
838
1089
|
keywords,
|
|
839
1090
|
mode,
|
|
@@ -919,6 +1170,73 @@ function normalizeAssetPath(value) {
|
|
|
919
1170
|
}
|
|
920
1171
|
|
|
921
1172
|
// src/components.ts
|
|
1173
|
+
function normalizeIndentedFences(content) {
|
|
1174
|
+
const lines = content.split("\n");
|
|
1175
|
+
let active;
|
|
1176
|
+
let ordinaryFence;
|
|
1177
|
+
let indentedJsx;
|
|
1178
|
+
let listIndent;
|
|
1179
|
+
return lines.map((line) => {
|
|
1180
|
+
const trimmed = line.trimStart();
|
|
1181
|
+
if (indentedJsx) {
|
|
1182
|
+
const normalized2 = line.startsWith(indentedJsx.indent) ? line.slice(indentedJsx.indent.length) : line;
|
|
1183
|
+
if (normalized2.trimStart().startsWith(`</${indentedJsx.tag}>`)) indentedJsx = void 0;
|
|
1184
|
+
return normalized2;
|
|
1185
|
+
}
|
|
1186
|
+
if (ordinaryFence) {
|
|
1187
|
+
const closing2 = trimmed.match(/^(`{3,}|~{3,})\s*$/)?.[1];
|
|
1188
|
+
if (closing2 && closing2[0] === ordinaryFence[0] && closing2.length >= ordinaryFence.length) ordinaryFence = void 0;
|
|
1189
|
+
return line;
|
|
1190
|
+
}
|
|
1191
|
+
if (!active) {
|
|
1192
|
+
const listItem = line.match(/^([ \t]*)(?:[-*+]|\d+[.)])\s/);
|
|
1193
|
+
const indentation = line.length - trimmed.length;
|
|
1194
|
+
if (listItem) listIndent = listItem[1].length;
|
|
1195
|
+
else if (trimmed && listIndent !== void 0 && indentation <= listIndent) listIndent = void 0;
|
|
1196
|
+
const jsx = line.match(/^([ \t]{2,})<([A-Z][\w.]*)\b[^>]*>\s*$/);
|
|
1197
|
+
if (jsx && listIndent !== void 0 && jsx[1].length > listIndent && !trimmed.endsWith("/>")) {
|
|
1198
|
+
indentedJsx = { tag: jsx[2], indent: jsx[1] };
|
|
1199
|
+
return line.slice(jsx[1].length);
|
|
1200
|
+
}
|
|
1201
|
+
const match = line.match(/^([ \t]{4,})(`{3,}|~{3,})/);
|
|
1202
|
+
if (match) {
|
|
1203
|
+
active = { indent: match[1], marker: match[2] };
|
|
1204
|
+
return line.slice(match[1].length);
|
|
1205
|
+
}
|
|
1206
|
+
const ordinary = line.match(/^[ \t]{0,3}(`{3,}|~{3,})/);
|
|
1207
|
+
if (ordinary) ordinaryFence = ordinary[1];
|
|
1208
|
+
return line;
|
|
1209
|
+
}
|
|
1210
|
+
const normalized = line.startsWith(active.indent) ? line.slice(active.indent.length) : line;
|
|
1211
|
+
const closing = normalized.trimStart().match(/^(`{3,}|~{3,})\s*$/)?.[1];
|
|
1212
|
+
if (closing && closing[0] === active.marker[0] && closing.length >= active.marker.length) active = void 0;
|
|
1213
|
+
return normalized;
|
|
1214
|
+
}).join("\n");
|
|
1215
|
+
}
|
|
1216
|
+
function staticServerHelper(path, imported, local) {
|
|
1217
|
+
if (imported === "default") return null;
|
|
1218
|
+
const parsed = ts.createSourceFile(path, readFileSync(path, "utf8"), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
1219
|
+
const isLiteral = (node) => {
|
|
1220
|
+
if (ts.isParenthesizedExpression(node)) return isLiteral(node.expression);
|
|
1221
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) || ts.isNumericLiteral(node)) return true;
|
|
1222
|
+
if ([ts.SyntaxKind.TrueKeyword, ts.SyntaxKind.FalseKeyword, ts.SyntaxKind.NullKeyword].includes(node.kind)) return true;
|
|
1223
|
+
if (ts.isPrefixUnaryExpression(node)) return [ts.SyntaxKind.PlusToken, ts.SyntaxKind.MinusToken].includes(node.operator) && ts.isNumericLiteral(node.operand);
|
|
1224
|
+
if (ts.isArrayLiteralExpression(node)) return node.elements.every((element) => ts.isExpression(element) && isLiteral(element));
|
|
1225
|
+
if (ts.isObjectLiteralExpression(node)) return node.properties.every((property) => ts.isPropertyAssignment(property) && (ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) || ts.isNumericLiteral(property.name)) && isLiteral(property.initializer));
|
|
1226
|
+
return false;
|
|
1227
|
+
};
|
|
1228
|
+
for (const statement of parsed.statements) {
|
|
1229
|
+
if (!ts.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)) continue;
|
|
1230
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1231
|
+
if (!ts.isIdentifier(declaration.name) || declaration.name.text !== imported || !declaration.initializer || !ts.isArrowFunction(declaration.initializer)) continue;
|
|
1232
|
+
const arrow = declaration.initializer;
|
|
1233
|
+
if (arrow.parameters.length > 0) continue;
|
|
1234
|
+
const result = ts.isBlock(arrow.body) ? arrow.body.statements.length === 1 && ts.isReturnStatement(arrow.body.statements[0]) ? arrow.body.statements[0].expression : void 0 : arrow.body;
|
|
1235
|
+
if (result && isLiteral(result)) return `export const ${local} = () => (${result.getText(parsed)});`;
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
922
1240
|
var YOUTUBE_VIDEO_ID = /^[\w-]{1,64}$/;
|
|
923
1241
|
var YOUTUBE_ID_PACKAGES = /* @__PURE__ */ new Set(["react-lite-youtube-embed", "react-youtube", "@justinribeiro/lite-youtube"]);
|
|
924
1242
|
var YOUTUBE_URL = /(?:youtube\.com\/(?:watch\?v=|embed\/|shorts\/)|youtu\.be\/)([\w-]{1,64})/i;
|
|
@@ -1474,7 +1792,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1474
1792
|
function transform(raw, currentFile) {
|
|
1475
1793
|
const parsedFrontmatter = parseFrontmatter(raw).content;
|
|
1476
1794
|
const frontmatter = raw.slice(0, raw.length - parsedFrontmatter.length);
|
|
1477
|
-
const content = normalizeHtmlComments(parsedFrontmatter);
|
|
1795
|
+
const content = normalizeIndentedFences(normalizeHtmlComments(parsedFrontmatter));
|
|
1478
1796
|
let tree;
|
|
1479
1797
|
try {
|
|
1480
1798
|
tree = parser.parse(content);
|
|
@@ -1488,6 +1806,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1488
1806
|
const declarations = [];
|
|
1489
1807
|
const moduleImports = [];
|
|
1490
1808
|
const realPageImports = [];
|
|
1809
|
+
const serverPageDeclarations = [];
|
|
1491
1810
|
const sharedImportEdits = [];
|
|
1492
1811
|
let hasUnsupportedImports = false;
|
|
1493
1812
|
function hasExpressionReference(names, options = {}) {
|
|
@@ -1553,13 +1872,19 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1553
1872
|
const rawSpecifier = ts.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : "";
|
|
1554
1873
|
const specifier = rawSpecifier.startsWith("@site/") ? `/${rawSpecifier.slice("@site/".length)}` : rawSpecifier;
|
|
1555
1874
|
if (isScaffoldProvidedImport(specifier)) {
|
|
1875
|
+
if (specifier === "react" && bindings.length > 0 && bindings.every((binding) => REACT_GLOBALS.has(binding.imported)) && !hasExpressionReference(new Set(bindings.map((binding) => binding.local)))) {
|
|
1876
|
+
edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: "" });
|
|
1877
|
+
continue;
|
|
1878
|
+
}
|
|
1556
1879
|
moduleImports.push(statement.getText(ast));
|
|
1557
1880
|
continue;
|
|
1558
1881
|
}
|
|
1559
1882
|
if (!specifier.startsWith(".") && !specifier.startsWith("/")) {
|
|
1560
1883
|
if (SHARED_IMPORTS.has(specifier)) {
|
|
1561
|
-
|
|
1562
|
-
|
|
1884
|
+
const isUnusedReactHookImport = specifier === "react" && bindings.length > 0 && bindings.every((binding) => REACT_GLOBALS.has(binding.imported)) && !hasExpressionReference(new Set(bindings.map((binding) => binding.local)));
|
|
1885
|
+
if (!isUnusedReactHookImport) moduleImports.push(statement.getText(ast));
|
|
1886
|
+
if (isUnusedReactHookImport) edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: "" });
|
|
1887
|
+
else sharedImportEdits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: "" });
|
|
1563
1888
|
} else if (specifier.startsWith("@theme/") || specifier.startsWith("@docusaurus/")) {
|
|
1564
1889
|
edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: "" });
|
|
1565
1890
|
} else if (hasExpressionReference(new Set(bindings.map((binding) => binding.local)))) {
|
|
@@ -1595,18 +1920,19 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1595
1920
|
hasUnsupportedImports = true;
|
|
1596
1921
|
continue;
|
|
1597
1922
|
}
|
|
1598
|
-
if (bindings.some((binding) => !/^[A-Z]/.test(binding.local))) {
|
|
1599
|
-
warn("MDX imports containing non-component values require manual migration; the import was preserved.", currentFile);
|
|
1600
|
-
hasUnsupportedImports = true;
|
|
1601
|
-
continue;
|
|
1602
|
-
}
|
|
1603
1923
|
try {
|
|
1604
|
-
const
|
|
1924
|
+
const sourcePath = resolveDependency(specifier, currentFile);
|
|
1925
|
+
const path = copyGraph(sourcePath);
|
|
1605
1926
|
const isSvgUsedAsTag = extname(path).toLowerCase() === ".svg" && bindings.some((binding) => usedAsJsxTagName(binding.local));
|
|
1606
1927
|
for (const binding of bindings) {
|
|
1607
1928
|
const registerPath = isSvgUsedAsTag ? wrapSvgAsComponent(path) : path;
|
|
1608
1929
|
const registerImported = isSvgUsedAsTag ? "default" : binding.imported;
|
|
1609
1930
|
if (!isSvgUsedAsTag && hasExpressionReference(/* @__PURE__ */ new Set([binding.local]))) {
|
|
1931
|
+
const helper = staticServerHelper(sourcePath, binding.imported, binding.local);
|
|
1932
|
+
if (helper) {
|
|
1933
|
+
serverPageDeclarations.push(helper);
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1610
1936
|
const aliasSpecifier = portableSpecifier(`@/${registerPath.replace(/^src\//, "").replace(/\\/g, "/")}`);
|
|
1611
1937
|
realPageImports.push(`import { ${registerImported} as ${binding.local} } from ${JSON.stringify(aliasSpecifier)};`);
|
|
1612
1938
|
continue;
|
|
@@ -1617,6 +1943,15 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1617
1943
|
}
|
|
1618
1944
|
edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: "" });
|
|
1619
1945
|
} catch (error) {
|
|
1946
|
+
if (hasExpressionReference(new Set(bindings.map((binding) => binding.local)))) {
|
|
1947
|
+
warnings.push({
|
|
1948
|
+
code: "skipped-file",
|
|
1949
|
+
message: `This page was excluded because local import ${JSON.stringify(rawSpecifier)} could not be copied and is used in an expression: ${error instanceof Error ? error.message : "unsupported dependency"}.`,
|
|
1950
|
+
source: relative2(root, currentFile).replace(/\\/g, "/")
|
|
1951
|
+
});
|
|
1952
|
+
hasUnsupportedImports = true;
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1620
1955
|
const failedPackage = error instanceof Error ? error.message.match(/^external package (\S+) requires manual installation and review$/)?.[1] : void 0;
|
|
1621
1956
|
if (failedPackage && YOUTUBE_ID_PACKAGES.has(failedPackage)) {
|
|
1622
1957
|
for (const binding of bindings) unsupportedImports.set(binding.local, failedPackage);
|
|
@@ -1758,9 +2093,64 @@ ${inlineSource}` });
|
|
|
1758
2093
|
}
|
|
1759
2094
|
edits.push(...declarations.map(({ start, end }) => ({ start, end, value: "" })), ...sharedImportEdits);
|
|
1760
2095
|
}
|
|
2096
|
+
const pageFunctions = functionDeclaredNames(content);
|
|
2097
|
+
const passiveRoots = [];
|
|
2098
|
+
const wrappedRoots = /* @__PURE__ */ new Set();
|
|
2099
|
+
walk(tree, (node) => {
|
|
2100
|
+
if (!node.name || !aliases.has(node.name) || !functionValuedAttribute(node, pageFunctions) || node.position?.start.offset === void 0 || node.position.end.offset === void 0) return;
|
|
2101
|
+
if (passiveRoots.some((root2) => node.position.start.offset >= root2.position.start.offset && node.position.end.offset <= root2.position.end.offset)) return;
|
|
2102
|
+
const functionAttributes = (node.attributes ?? []).filter((attribute) => {
|
|
2103
|
+
const value = attribute.value && typeof attribute.value === "object" ? attribute.value.value?.trim() : void 0;
|
|
2104
|
+
return !!value && pageFunctions.has(value);
|
|
2105
|
+
});
|
|
2106
|
+
if (functionAttributes.length === 1 && functionAttributes[0].name && functionAttributes[0].position?.start.offset !== void 0 && functionAttributes[0].position.end.offset !== void 0) {
|
|
2107
|
+
const functionName = functionAttributes[0].value.value.trim();
|
|
2108
|
+
const declaration = declarations.find((entry) => new RegExp(`^export const ${functionName}\\s*=`).test(entry.source));
|
|
2109
|
+
const safeDeclaration = declaration?.source.match(/^export const \w+\s*=\s*\(\s*\{\s*children(?:\s*,\s*\.\.\.props)?\s*\}\s*\)\s*=>\s*\([\s\S]*\)\s*;?$/) && !/\b(?:window|document|process|fetch|use[A-Z]\w*)\b/.test(declaration.source) && [...declaration.source.matchAll(/<([A-Z]\w*)\b/g)].every((match) => match[1] === "CodeBlock") && [...declaration.source.matchAll(/\{([^{}]*)\}/g)].every((match) => ["children", "...props", "children, ...props"].includes(match[1].trim()));
|
|
2110
|
+
const binding = registrations.get(aliases.get(node.name));
|
|
2111
|
+
if (safeDeclaration && binding) {
|
|
2112
|
+
const fallbackDeclaration = declaration.source.replace(/<CodeBlock\b[^>]*>/g, "<pre><code>").replace(/<\/CodeBlock>/g, "</code></pre>");
|
|
2113
|
+
const wrapperPath = `${destinationRoot}/callback-${hash(`${currentFile}:${node.position.start.offset}`)}.jsx`;
|
|
2114
|
+
const imported = binding.imported === "default" ? "default" : binding.imported;
|
|
2115
|
+
const importClause = imported === "default" ? "import Original" : `import { ${imported} as Original }`;
|
|
2116
|
+
const specifier = portableSpecifier(`./${relative2(destinationRoot, binding.path).replace(/\\/g, "/")}`);
|
|
2117
|
+
const wrapperName = register(wrapperPath, "CallbackWrapper");
|
|
2118
|
+
copied.set(wrapperPath, { path: wrapperPath, content: [
|
|
2119
|
+
"'use client';",
|
|
2120
|
+
`${importClause} from ${JSON.stringify(specifier)};`,
|
|
2121
|
+
fallbackDeclaration,
|
|
2122
|
+
`export function CallbackWrapper({ children, ...props }) { return <Original {...props} ${functionAttributes[0].name}={${functionName}}>{children}</Original>; }`
|
|
2123
|
+
].join("\n") });
|
|
2124
|
+
const start = node.position.start.offset;
|
|
2125
|
+
const end = node.position.end.offset;
|
|
2126
|
+
const source2 = content.slice(start, end);
|
|
2127
|
+
const opening = source2.indexOf(`<${node.name}`);
|
|
2128
|
+
const closing = source2.lastIndexOf(`</${node.name}`);
|
|
2129
|
+
if (opening >= 0) edits.push({ start: start + opening + 1, end: start + opening + 1 + node.name.length, value: wrapperName });
|
|
2130
|
+
if (closing >= 0) edits.push({ start: start + closing + 2, end: start + closing + 2 + node.name.length, value: wrapperName });
|
|
2131
|
+
edits.push({ start: functionAttributes[0].position.start.offset, end: functionAttributes[0].position.end.offset, value: "" });
|
|
2132
|
+
wrappedRoots.add(node);
|
|
2133
|
+
if (declaration.source.includes("<CodeBlock")) warn(`Interactive <${node.name}> was retained with a basic code block in place of Mintlify's CodeBlock.`, currentFile);
|
|
2134
|
+
return;
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
const source = content.slice(node.position.start.offset, node.position.end.offset);
|
|
2138
|
+
const openingEnd = source.indexOf(">");
|
|
2139
|
+
const closingStart = source.lastIndexOf(`</${node.name}`);
|
|
2140
|
+
const children = openingEnd >= 0 && closingStart > openingEnd ? source.slice(openingEnd + 1, closingStart) : "";
|
|
2141
|
+
edits.push({
|
|
2142
|
+
start: node.position.start.offset,
|
|
2143
|
+
end: node.position.end.offset,
|
|
2144
|
+
value: `<div data-migration-interactive-fallback="${node.name}">${children}</div>`
|
|
2145
|
+
});
|
|
2146
|
+
passiveRoots.push(node);
|
|
2147
|
+
warn(`Interactive <${node.name}> passes a page-local function into a client component; its child content was retained without the interactive control. Move the callback into a client component to restore it.`, currentFile);
|
|
2148
|
+
});
|
|
1761
2149
|
walk(tree, (node) => {
|
|
1762
2150
|
const replacement = node.name ? aliases.get(node.name) : void 0;
|
|
1763
2151
|
if (!replacement || node.position?.start.offset === void 0 || node.position.end.offset === void 0) return;
|
|
2152
|
+
if (wrappedRoots.has(node)) return;
|
|
2153
|
+
if (passiveRoots.some((root2) => node.position.start.offset >= root2.position.start.offset && node.position.end.offset <= root2.position.end.offset)) return;
|
|
1764
2154
|
const start = node.position.start.offset;
|
|
1765
2155
|
const end = node.position.end.offset;
|
|
1766
2156
|
if (extracted.some((item) => start >= item.node.position.start.offset && end <= item.node.position.end.offset)) return;
|
|
@@ -1780,7 +2170,8 @@ ${inlineSource}` });
|
|
|
1780
2170
|
edits.push({ start, end, value });
|
|
1781
2171
|
});
|
|
1782
2172
|
const rendered = applyReplacements(content, edits);
|
|
1783
|
-
const
|
|
2173
|
+
const pageStatements = [.../* @__PURE__ */ new Set([...realPageImports, ...serverPageDeclarations])];
|
|
2174
|
+
const realImportsBlock = pageStatements.length ? `${pageStatements.join("\n")}
|
|
1784
2175
|
|
|
1785
2176
|
` : "";
|
|
1786
2177
|
return frontmatter + realImportsBlock + rendered;
|
|
@@ -2404,6 +2795,7 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
2404
2795
|
}) : [];
|
|
2405
2796
|
let tabs = [];
|
|
2406
2797
|
let i18n;
|
|
2798
|
+
const localizedNavigation = {};
|
|
2407
2799
|
const projectionTrace = {};
|
|
2408
2800
|
if (languages.length > 0) {
|
|
2409
2801
|
const defaultLanguage = languages.find((entry) => entry.default === true) ?? languages[0];
|
|
@@ -2439,8 +2831,11 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
2439
2831
|
if (language === defaultLanguage) {
|
|
2440
2832
|
tabs = languageTabs;
|
|
2441
2833
|
for (const reference of references) defaultPageIds.add(reference.navigationId);
|
|
2834
|
+
} else if (languageTabs.length > 0) {
|
|
2835
|
+
localizedNavigation[locale] = languageTabs;
|
|
2442
2836
|
}
|
|
2443
2837
|
}
|
|
2838
|
+
if (Object.keys(localizedNavigation).length > 0) i18n.navigation = localizedNavigation;
|
|
2444
2839
|
} else {
|
|
2445
2840
|
const context = { references, seenReferences, warnings, warningKeys, pathPrefix: options.pathPrefix };
|
|
2446
2841
|
tabs = convertContainerToTabs(navigation, context, "Documentation", projectionTrace);
|
|
@@ -2598,11 +2993,18 @@ function isDocumentationExtension(filename) {
|
|
|
2598
2993
|
return [".md", ".mdx", ".rst", ".txt"].includes(extname2(filename).toLowerCase());
|
|
2599
2994
|
}
|
|
2600
2995
|
function pruneMissingNavigationPages(config, availableIds) {
|
|
2601
|
-
const pruneNodes = (nodes) =>
|
|
2602
|
-
|
|
2603
|
-
|
|
2604
|
-
|
|
2605
|
-
|
|
2996
|
+
const pruneNodes = (nodes) => {
|
|
2997
|
+
const seenSiblings = /* @__PURE__ */ new Set();
|
|
2998
|
+
return nodes.flatMap((node) => {
|
|
2999
|
+
if (typeof node === "string") {
|
|
3000
|
+
if (!availableIds.has(node) || seenSiblings.has(node)) return [];
|
|
3001
|
+
seenSiblings.add(node);
|
|
3002
|
+
return [node];
|
|
3003
|
+
}
|
|
3004
|
+
const pages = pruneNodes(node.pages);
|
|
3005
|
+
return pages.length > 0 ? [{ ...node, pages }] : [];
|
|
3006
|
+
});
|
|
3007
|
+
};
|
|
2606
3008
|
const tabs = config.tabs.flatMap((tab) => {
|
|
2607
3009
|
const hadPages = (tab.pages?.length ?? 0) > 0 || (tab.groups?.length ?? 0) > 0;
|
|
2608
3010
|
const pages = tab.pages ? pruneNodes(tab.pages) : void 0;
|
|
@@ -2695,7 +3097,7 @@ function resolveDocusaurusPageIdentity(sourcePath, frontmatter, fallback) {
|
|
|
2695
3097
|
function sourceReferenceKey(value) {
|
|
2696
3098
|
return value.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.(?:mdx?)$/i, "").replace(/\/$/, "");
|
|
2697
3099
|
}
|
|
2698
|
-
function rewriteDocusaurusLinks(body, current, descriptors) {
|
|
3100
|
+
function rewriteDocusaurusLinks(body, current, descriptors, options = {}) {
|
|
2699
3101
|
const routes = /* @__PURE__ */ new Map();
|
|
2700
3102
|
for (const descriptor of descriptors) {
|
|
2701
3103
|
const sourceKey = sourceReferenceKey(descriptor.sourcePath);
|
|
@@ -2711,26 +3113,52 @@ function rewriteDocusaurusLinks(body, current, descriptors) {
|
|
|
2711
3113
|
const suffixIndex = target.search(/[?#]/);
|
|
2712
3114
|
const path = suffixIndex >= 0 ? target.slice(0, suffixIndex) : target;
|
|
2713
3115
|
const suffix = suffixIndex >= 0 ? target.slice(suffixIndex) : "";
|
|
2714
|
-
const candidates = path.startsWith("/") ? [
|
|
3116
|
+
const candidates = path.startsWith("/") ? [
|
|
3117
|
+
sourceReferenceKey(path),
|
|
3118
|
+
sourceReferenceKey(path).replace(/^docs\//, ""),
|
|
3119
|
+
sourceReferenceKey(path).replace(/^docs\/(?:next|current|latest)\//, "")
|
|
3120
|
+
] : [
|
|
2715
3121
|
sourceReferenceKey(posix.normalize(posix.join(currentDirectory, path))),
|
|
2716
3122
|
sourceReferenceKey(path)
|
|
2717
3123
|
];
|
|
2718
3124
|
const route = candidates.map((candidate) => routes.get(candidate)).find(Boolean);
|
|
2719
|
-
if (!route)
|
|
3125
|
+
if (!route) {
|
|
3126
|
+
if (path.startsWith("/") && path !== "/" && options.sourceOrigin) {
|
|
3127
|
+
options.onExternalLink?.(target);
|
|
3128
|
+
return new URL(target, options.sourceOrigin).toString();
|
|
3129
|
+
}
|
|
3130
|
+
return target;
|
|
3131
|
+
}
|
|
2720
3132
|
return `${route === "introduction" ? "/" : `/${route}`}${suffix}`;
|
|
2721
3133
|
}
|
|
2722
3134
|
let codeFence = null;
|
|
2723
3135
|
return body.split("\n").map((line) => {
|
|
2724
|
-
const fence = line.match(/^\s
|
|
3136
|
+
const fence = line.match(/^\s{0,3}(`{3,}|~{3,})/);
|
|
2725
3137
|
if (fence) {
|
|
2726
|
-
if (!codeFence) codeFence = fence[1]
|
|
2727
|
-
else if (fence[1][0] === codeFence) codeFence = null;
|
|
3138
|
+
if (!codeFence) codeFence = fence[1];
|
|
3139
|
+
else if (fence[1][0] === codeFence[0] && fence[1].length >= codeFence.length && /^\s*$/.test(line.slice(fence[0].length))) codeFence = null;
|
|
2728
3140
|
return line;
|
|
2729
3141
|
}
|
|
2730
3142
|
if (codeFence) return line;
|
|
2731
|
-
return line.replace(/(\]\()([^\s)]+)(?=[\s)]|$)/g, (_match, prefix, target) => `${prefix}${rewriteTarget(target)}`).replace(/(\bhref=")([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${rewriteTarget(target)}${suffix}`)
|
|
3143
|
+
return line.replace(/(\]\()([^\s)]+)(?=[\s)]|$)/g, (_match, prefix, target) => `${prefix}${rewriteTarget(target)}`).replace(/(\bhref=")([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${rewriteTarget(target)}${suffix}`).replace(/^(\s{0,3}\[[^\]]+\]:\s*)(<[^>]+>|\S+)/, (_match, prefix, destination) => {
|
|
3144
|
+
const bracketed = destination.startsWith("<") && destination.endsWith(">");
|
|
3145
|
+
const target = bracketed ? destination.slice(1, -1) : destination;
|
|
3146
|
+
const rewritten = rewriteTarget(target);
|
|
3147
|
+
return `${prefix}${bracketed ? `<${rewritten}>` : rewritten}`;
|
|
3148
|
+
});
|
|
2732
3149
|
}).join("\n");
|
|
2733
3150
|
}
|
|
3151
|
+
function readDocusaurusSiteOrigin(repositoryRoot) {
|
|
3152
|
+
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
3153
|
+
if (!configPath) return void 0;
|
|
3154
|
+
const address = readBoundedText(configPath).match(/\burl['"]?\s*:\s*(['"])(https?:\/\/[^'"]+)\1/)?.[2];
|
|
3155
|
+
if (!address) return void 0;
|
|
3156
|
+
try {
|
|
3157
|
+
return new URL(address).origin;
|
|
3158
|
+
} catch {
|
|
3159
|
+
return void 0;
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
2734
3162
|
function readBoundedText(path) {
|
|
2735
3163
|
if (lstatSync3(path).size > MAX_CONFIG_BYTES) {
|
|
2736
3164
|
throw new Error("Docusaurus sidebar config exceeded the 1 MB static-parser limit.");
|
|
@@ -2892,6 +3320,14 @@ function configuredSidebarPath(repositoryRoot) {
|
|
|
2892
3320
|
const path = resolveWithin(repositoryRoot, filename);
|
|
2893
3321
|
if (!existsSync3(path)) continue;
|
|
2894
3322
|
const source = readBoundedText(path);
|
|
3323
|
+
for (const docs of source.matchAll(/\bdocs\s*:\s*/g)) {
|
|
3324
|
+
const object = matchingObjectLiteral(source, (docs.index ?? 0) + docs[0].length);
|
|
3325
|
+
const candidate2 = object && staticStringField(object, "sidebarPath")?.replace(/^\.\//, "");
|
|
3326
|
+
if (candidate2) {
|
|
3327
|
+
const resolved2 = resolveWithin(repositoryRoot, candidate2);
|
|
3328
|
+
if (existsSync3(resolved2) && lstatSync3(resolved2).isFile()) return candidate2;
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
2895
3331
|
const match = source.match(/\bsidebarPath\s*:\s*(?:require\.resolve\(\s*)?(['"])([^'"]+)\1/);
|
|
2896
3332
|
if (!match) continue;
|
|
2897
3333
|
const candidate = match[2].replace(/^\.\//, "");
|
|
@@ -2900,9 +3336,12 @@ function configuredSidebarPath(repositoryRoot) {
|
|
|
2900
3336
|
}
|
|
2901
3337
|
return null;
|
|
2902
3338
|
}
|
|
2903
|
-
function readDocusaurusSidebars(repositoryRoot) {
|
|
2904
|
-
const configured = configuredSidebarPath(repositoryRoot);
|
|
2905
|
-
const sourcePath = [configured, ...SIDEBAR_FILENAMES].filter((value) => Boolean(value)).find((candidate) =>
|
|
3339
|
+
function readDocusaurusSidebars(repositoryRoot, versionedSidebarPath) {
|
|
3340
|
+
const configured = versionedSidebarPath ?? configuredSidebarPath(repositoryRoot);
|
|
3341
|
+
const sourcePath = [configured, ...versionedSidebarPath ? [] : SIDEBAR_FILENAMES].filter((value) => Boolean(value)).find((candidate) => {
|
|
3342
|
+
const path = resolveWithin(repositoryRoot, candidate);
|
|
3343
|
+
return existsSync3(path) && lstatSync3(path).isFile();
|
|
3344
|
+
});
|
|
2906
3345
|
if (!sourcePath) return null;
|
|
2907
3346
|
const absolutePath = resolveWithin(repositoryRoot, sourcePath);
|
|
2908
3347
|
const source = readBoundedText(absolutePath);
|
|
@@ -3070,15 +3509,156 @@ function readDocusaurusThemeColor(repositoryRoot) {
|
|
|
3070
3509
|
if (lightMode) colors.dark = lightMode;
|
|
3071
3510
|
return Object.keys(colors).length > 0 ? colors : void 0;
|
|
3072
3511
|
}
|
|
3073
|
-
function
|
|
3512
|
+
function propertyStart(source, name) {
|
|
3513
|
+
const pattern = new RegExp(`(?:^|[,{\\n])\\s*["']?${name}["']?\\s*:\\s*`, "m");
|
|
3514
|
+
const match = pattern.exec(source);
|
|
3515
|
+
return match ? match.index + match[0].length : -1;
|
|
3516
|
+
}
|
|
3517
|
+
function staticStringBindings(source) {
|
|
3518
|
+
const bindings = /* @__PURE__ */ new Map();
|
|
3519
|
+
for (const match of source.matchAll(/\bconst\s+([A-Z][A-Z0-9_]*)\s*=\s*((?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'))/g)) {
|
|
3520
|
+
try {
|
|
3521
|
+
const value = JSON5.parse(match[2]);
|
|
3522
|
+
if (typeof value === "string") bindings.set(match[1], value);
|
|
3523
|
+
} catch {
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
return bindings;
|
|
3527
|
+
}
|
|
3528
|
+
function staticArrayField(source, name) {
|
|
3529
|
+
const start = propertyStart(source, name);
|
|
3530
|
+
return start < 0 ? null : matchingArrayLiteral(source, start);
|
|
3531
|
+
}
|
|
3532
|
+
function staticObjectEntries(array) {
|
|
3533
|
+
if (!array) return [];
|
|
3534
|
+
const entries = [];
|
|
3535
|
+
let afterDelimiter = 1;
|
|
3536
|
+
let quote = "";
|
|
3537
|
+
let lineComment = false;
|
|
3538
|
+
let blockComment = false;
|
|
3539
|
+
for (let index = 1; index < array.length - 1; index++) {
|
|
3540
|
+
const char = array[index];
|
|
3541
|
+
const next = array[index + 1];
|
|
3542
|
+
if (lineComment) {
|
|
3543
|
+
if (char === "\n") lineComment = false;
|
|
3544
|
+
continue;
|
|
3545
|
+
}
|
|
3546
|
+
if (blockComment) {
|
|
3547
|
+
if (char === "*" && next === "/") {
|
|
3548
|
+
blockComment = false;
|
|
3549
|
+
index++;
|
|
3550
|
+
}
|
|
3551
|
+
continue;
|
|
3552
|
+
}
|
|
3553
|
+
if (quote) {
|
|
3554
|
+
if (char === "\\") {
|
|
3555
|
+
index++;
|
|
3556
|
+
continue;
|
|
3557
|
+
}
|
|
3558
|
+
if (char === quote) quote = "";
|
|
3559
|
+
continue;
|
|
3560
|
+
}
|
|
3561
|
+
if (char === "/" && next === "/") {
|
|
3562
|
+
lineComment = true;
|
|
3563
|
+
index++;
|
|
3564
|
+
continue;
|
|
3565
|
+
}
|
|
3566
|
+
if (char === "/" && next === "*") {
|
|
3567
|
+
blockComment = true;
|
|
3568
|
+
index++;
|
|
3569
|
+
continue;
|
|
3570
|
+
}
|
|
3571
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
3572
|
+
quote = char;
|
|
3573
|
+
continue;
|
|
3574
|
+
}
|
|
3575
|
+
if (char === ",") {
|
|
3576
|
+
afterDelimiter = index + 1;
|
|
3577
|
+
continue;
|
|
3578
|
+
}
|
|
3579
|
+
if (char !== "{") continue;
|
|
3580
|
+
const literal = matchingObjectLiteral(array, index);
|
|
3581
|
+
if (!literal) continue;
|
|
3582
|
+
const lead = array.slice(afterDelimiter, index).replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*/g, "").trim();
|
|
3583
|
+
if (!lead) entries.push(literal);
|
|
3584
|
+
index += literal.length - 1;
|
|
3585
|
+
}
|
|
3586
|
+
return entries;
|
|
3587
|
+
}
|
|
3588
|
+
function staticStringField(source, name, bindings) {
|
|
3589
|
+
const start = propertyStart(source, name);
|
|
3590
|
+
if (start < 0) return void 0;
|
|
3591
|
+
const quoted = source.slice(start).match(/^("(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')/);
|
|
3592
|
+
if (!quoted) {
|
|
3593
|
+
const binding = source.slice(start).match(/^([A-Z][A-Z0-9_]*)\b/)?.[1];
|
|
3594
|
+
return binding ? bindings?.get(binding) : void 0;
|
|
3595
|
+
}
|
|
3596
|
+
try {
|
|
3597
|
+
const parsed = JSON5.parse(quoted[1]);
|
|
3598
|
+
return typeof parsed === "string" ? parsed : void 0;
|
|
3599
|
+
} catch {
|
|
3600
|
+
return void 0;
|
|
3601
|
+
}
|
|
3602
|
+
}
|
|
3603
|
+
function readDocusaurusSiteSettings(repositoryRoot) {
|
|
3604
|
+
const empty = { docsRouteBasePath: "docs", navbarLinks: [], footerLinks: [] };
|
|
3074
3605
|
const configPath = findDocusaurusConfigPath(repositoryRoot);
|
|
3075
|
-
if (!configPath) return
|
|
3076
|
-
const
|
|
3077
|
-
const
|
|
3078
|
-
const
|
|
3079
|
-
const
|
|
3080
|
-
const
|
|
3081
|
-
|
|
3606
|
+
if (!configPath) return empty;
|
|
3607
|
+
const source = readBoundedText(configPath);
|
|
3608
|
+
const bindings = staticStringBindings(source);
|
|
3609
|
+
const presets = staticArrayField(source, "presets");
|
|
3610
|
+
const classicDocs = presets && [...presets.matchAll(/["']?docs["']?\s*:\s*/g)].map((match) => matchingObjectLiteral(presets, (match.index ?? 0) + match[0].length)).find((object) => Boolean(object));
|
|
3611
|
+
const docsRouteBasePath = classicDocs && staticStringField(classicDocs, "routeBasePath") || "docs";
|
|
3612
|
+
const themeStart = propertyStart(source, "themeConfig");
|
|
3613
|
+
const theme = themeStart < 0 ? null : matchingObjectLiteral(source, themeStart);
|
|
3614
|
+
const navbarStart = theme ? propertyStart(theme, "navbar") : -1;
|
|
3615
|
+
const navbar = navbarStart < 0 ? null : matchingObjectLiteral(theme, navbarStart);
|
|
3616
|
+
const logoStart = navbar ? propertyStart(navbar, "logo") : -1;
|
|
3617
|
+
const logo = logoStart < 0 ? null : matchingObjectLiteral(navbar, logoStart);
|
|
3618
|
+
const footerStart = theme ? propertyStart(theme, "footer") : -1;
|
|
3619
|
+
const footer = footerStart < 0 ? null : matchingObjectLiteral(theme, footerStart);
|
|
3620
|
+
const navbarLinks = navbar ? staticObjectEntries(staticArrayField(navbar, "items")).flatMap((item) => {
|
|
3621
|
+
if (propertyStart(item, "items") >= 0 || propertyStart(item, "dropdownItemsAfter") >= 0) return [];
|
|
3622
|
+
const docsPluginId = staticStringField(item, "docsPluginId", bindings);
|
|
3623
|
+
const href = staticStringField(item, "to", bindings) ?? staticStringField(item, "href", bindings);
|
|
3624
|
+
const docId = staticStringField(item, "docId", bindings);
|
|
3625
|
+
const sidebarId = staticStringField(item, "sidebarId", bindings);
|
|
3626
|
+
const label = staticStringField(item, "label", bindings) ?? staticStringField(item, "aria-label", bindings);
|
|
3627
|
+
return label && (href || docId || sidebarId || docsPluginId) ? [{
|
|
3628
|
+
label,
|
|
3629
|
+
...href ? { href } : {},
|
|
3630
|
+
...docId ? { docId } : {},
|
|
3631
|
+
...sidebarId ? { sidebarId } : {},
|
|
3632
|
+
...docsPluginId ? { docsPluginId } : {}
|
|
3633
|
+
}] : [];
|
|
3634
|
+
}) : [];
|
|
3635
|
+
const footerLinks = footer ? staticObjectEntries(staticArrayField(footer, "links")).flatMap((column) => {
|
|
3636
|
+
const heading = staticStringField(column, "title", bindings);
|
|
3637
|
+
if (!heading) return [];
|
|
3638
|
+
const items = staticObjectEntries(staticArrayField(column, "items")).flatMap((item) => {
|
|
3639
|
+
const href = staticStringField(item, "to", bindings) ?? staticStringField(item, "href", bindings);
|
|
3640
|
+
const label = staticStringField(item, "label", bindings);
|
|
3641
|
+
return label && href ? [{ label, href }] : [];
|
|
3642
|
+
});
|
|
3643
|
+
return items.length > 0 ? [{ heading, items }] : [];
|
|
3644
|
+
}) : [];
|
|
3645
|
+
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;
|
|
3646
|
+
return {
|
|
3647
|
+
name: staticStringField(source, "title"),
|
|
3648
|
+
description: staticStringField(source, "tagline"),
|
|
3649
|
+
docsRouteBasePath,
|
|
3650
|
+
...logo && staticStringField(logo, "src") ? {
|
|
3651
|
+
logo: {
|
|
3652
|
+
light: staticStringField(logo, "src"),
|
|
3653
|
+
...staticStringField(logo, "srcDark") ? { dark: staticStringField(logo, "srcDark") } : {},
|
|
3654
|
+
showTitle: Boolean(navbar && staticStringField(navbar, "title"))
|
|
3655
|
+
}
|
|
3656
|
+
} : {},
|
|
3657
|
+
...staticStringField(source, "favicon") ? { favicon: staticStringField(source, "favicon") } : {},
|
|
3658
|
+
navbarLinks,
|
|
3659
|
+
footerLinks,
|
|
3660
|
+
...copyright ? { copyright } : {}
|
|
3661
|
+
};
|
|
3082
3662
|
}
|
|
3083
3663
|
function autogeneratedItems(dirName, context) {
|
|
3084
3664
|
const normalizedDir = dirName === "." ? "" : normalizeDocId(dirName);
|
|
@@ -3165,19 +3745,19 @@ function projectDocusaurusNavigation(input) {
|
|
|
3165
3745
|
]]];
|
|
3166
3746
|
const tabs = sidebarEntries.flatMap(([sidebarId, value]) => {
|
|
3167
3747
|
const converted = convertItems(value, context);
|
|
3168
|
-
|
|
3169
|
-
const groups = converted.filter((entry) => typeof entry !== "string");
|
|
3170
|
-
if (loosePages.length > 0) groups.unshift({ group: "Overview", pages: loosePages });
|
|
3171
|
-
if (groups.length === 0) return [];
|
|
3748
|
+
if (converted.length === 0) return [];
|
|
3172
3749
|
return [{
|
|
3173
3750
|
tab: sidebarEntries.length === 1 ? "Documentation" : titleCase2(sidebarId),
|
|
3174
|
-
|
|
3751
|
+
// Docusaurus categories are disclosures, including at the sidebar
|
|
3752
|
+
// root. Keeping them as Thally root nodes preserves their collapsed
|
|
3753
|
+
// state and the authored interleaving of documents and categories.
|
|
3754
|
+
pages: converted
|
|
3175
3755
|
}];
|
|
3176
3756
|
});
|
|
3177
3757
|
const unreferenced = input.descriptors.filter((page) => !context.referencedNavigationIds.has(page.navigationId)).sort(descriptorSort).map((page) => page.navigationId);
|
|
3178
3758
|
if (unreferenced.length > 0) {
|
|
3179
|
-
if (tabs.length === 0) tabs.push({ tab: "Documentation",
|
|
3180
|
-
tabs[0].
|
|
3759
|
+
if (tabs.length === 0) tabs.push({ tab: "Documentation", pages: [] });
|
|
3760
|
+
tabs[0].pages?.push({ group: "Additional", pages: unreferenced });
|
|
3181
3761
|
}
|
|
3182
3762
|
return {
|
|
3183
3763
|
docsConfig: { tabs },
|
|
@@ -3188,10 +3768,37 @@ function projectDocusaurusNavigation(input) {
|
|
|
3188
3768
|
}
|
|
3189
3769
|
|
|
3190
3770
|
// src/fern.ts
|
|
3191
|
-
import { existsSync as existsSync4, lstatSync as lstatSync4, readFileSync as readFileSync4 } from "fs";
|
|
3192
|
-
import { dirname as dirname3, relative as relative4 } from "path";
|
|
3771
|
+
import { existsSync as existsSync4, lstatSync as lstatSync4, readFileSync as readFileSync4, readdirSync } from "fs";
|
|
3772
|
+
import { basename as basename2, dirname as dirname3, extname as extname4, relative as relative4 } from "path";
|
|
3773
|
+
import { load } from "cheerio";
|
|
3193
3774
|
import { parse as parseYaml3 } from "yaml";
|
|
3194
3775
|
var MAX_CONFIG_BYTES2 = 2e6;
|
|
3776
|
+
function announcementMarkdown(message) {
|
|
3777
|
+
const document = load(message);
|
|
3778
|
+
document("script, style, iframe, object, embed, svg, math").remove();
|
|
3779
|
+
const escapeText = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/([\\`*_\[\]{}()!])/g, "\\$1");
|
|
3780
|
+
const links = /* @__PURE__ */ new Map();
|
|
3781
|
+
document("a").each((index, element) => {
|
|
3782
|
+
const marker = `THALLYANNOUNCEMENTLINK${index}TOKEN`;
|
|
3783
|
+
const label = document(element).text().trim();
|
|
3784
|
+
const rawHref = document(element).attr("href")?.trim() ?? "";
|
|
3785
|
+
let href;
|
|
3786
|
+
if (/^https?:\/\//i.test(rawHref)) {
|
|
3787
|
+
try {
|
|
3788
|
+
href = new URL(rawHref).toString();
|
|
3789
|
+
} catch {
|
|
3790
|
+
}
|
|
3791
|
+
} else if (rawHref.startsWith("/") && !rawHref.startsWith("//") && !/[\\<>\s]/.test(rawHref)) {
|
|
3792
|
+
href = rawHref;
|
|
3793
|
+
}
|
|
3794
|
+
const destination = href?.replace(/\(/g, "%28").replace(/\)/g, "%29");
|
|
3795
|
+
links.set(marker, destination && label ? `[${escapeText(label)}](${destination})` : escapeText(label));
|
|
3796
|
+
document(element).replaceWith(marker);
|
|
3797
|
+
});
|
|
3798
|
+
let text = escapeText(document("body").text());
|
|
3799
|
+
for (const [marker, link] of links) text = text.replaceAll(marker, link);
|
|
3800
|
+
return text.trim();
|
|
3801
|
+
}
|
|
3195
3802
|
function objectValue3(value) {
|
|
3196
3803
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
3197
3804
|
}
|
|
@@ -3243,8 +3850,9 @@ function fernLabelSlug(label) {
|
|
|
3243
3850
|
}
|
|
3244
3851
|
function segmentFor(value, label, context) {
|
|
3245
3852
|
if (value["skip-slug"] === true) return null;
|
|
3246
|
-
|
|
3247
|
-
|
|
3853
|
+
if (typeof value.slug !== "string") return fernLabelSlug(label) || null;
|
|
3854
|
+
const explicit = value.slug.trim();
|
|
3855
|
+
if (!explicit) return null;
|
|
3248
3856
|
const slugified = fernBasicSlug(explicit);
|
|
3249
3857
|
if (context && explicit !== slugified && FERN_SEGMENT_ALIAS.test(explicit)) {
|
|
3250
3858
|
context.segmentAliases.set(slugified, explicit);
|
|
@@ -3257,28 +3865,99 @@ function uniqueNavigationId(base, context) {
|
|
|
3257
3865
|
while (context.seenNavigationIds.has(`${base}-${index}`)) index++;
|
|
3258
3866
|
return `${base}-${index}`;
|
|
3259
3867
|
}
|
|
3260
|
-
function registerPageAt(rawPath, base, context) {
|
|
3261
|
-
|
|
3262
|
-
|
|
3868
|
+
function registerPageAt(rawPath, base, context, navTitle, hidden = false) {
|
|
3869
|
+
let sourcePath;
|
|
3870
|
+
try {
|
|
3871
|
+
const absolute = resolveWithinRoot(
|
|
3872
|
+
context.pathPrefix ? resolveWithin(context.fernRoot, context.pathPrefix) : context.fernRoot,
|
|
3873
|
+
rawPath.trim(),
|
|
3874
|
+
context.repositoryRoot
|
|
3875
|
+
);
|
|
3876
|
+
sourcePath = relative4(context.fernRoot, absolute).replace(/\\/g, "/");
|
|
3877
|
+
} catch {
|
|
3878
|
+
warnOnce2(context, `fern-unsafe-page-${rawPath}`, `Fern page path "${rawPath}" escapes the repository and was skipped.`);
|
|
3879
|
+
return "";
|
|
3880
|
+
}
|
|
3263
3881
|
const navigationId = uniqueNavigationId(base || "introduction", context);
|
|
3264
3882
|
context.seenNavigationIds.add(navigationId);
|
|
3265
|
-
context.
|
|
3883
|
+
const isHidden = hidden || context.hiddenInherited === true;
|
|
3884
|
+
context.descriptors.push({ sourcePath, navigationId, ...navTitle ? { navTitle } : {}, ...isHidden ? { hidden: true } : {} });
|
|
3266
3885
|
const kept = context.sourcePathToNavigationId.get(sourcePath);
|
|
3267
|
-
if (kept)
|
|
3886
|
+
if (kept) {
|
|
3887
|
+
if (!isHidden) {
|
|
3888
|
+
const owner = context.descriptors.find((descriptor) => descriptor.navigationId === kept);
|
|
3889
|
+
if (owner) owner.hidden = false;
|
|
3890
|
+
}
|
|
3891
|
+
return kept;
|
|
3892
|
+
}
|
|
3268
3893
|
context.sourcePathToNavigationId.set(sourcePath, navigationId);
|
|
3269
3894
|
return navigationId;
|
|
3270
3895
|
}
|
|
3896
|
+
function registerFolder(object, parentSegments, context) {
|
|
3897
|
+
const rawPath = String(object.folder).trim();
|
|
3898
|
+
let folder;
|
|
3899
|
+
try {
|
|
3900
|
+
folder = resolveWithinRoot(
|
|
3901
|
+
context.pathPrefix ? resolveWithin(context.fernRoot, context.pathPrefix) : context.fernRoot,
|
|
3902
|
+
rawPath,
|
|
3903
|
+
context.repositoryRoot
|
|
3904
|
+
);
|
|
3905
|
+
} catch {
|
|
3906
|
+
warnOnce2(context, `fern-unsafe-folder-${rawPath}`, `Fern folder "${rawPath}" escapes the repository and was skipped.`);
|
|
3907
|
+
return null;
|
|
3908
|
+
}
|
|
3909
|
+
if (!existsSync4(folder) || !lstatSync4(folder).isDirectory()) {
|
|
3910
|
+
warnOnce2(context, `fern-missing-folder-${rawPath}`, `Fern folder "${rawPath}" does not exist and was skipped.`);
|
|
3911
|
+
return null;
|
|
3912
|
+
}
|
|
3913
|
+
const label = typeof object.title === "string" && object.title.trim() ? object.title.trim() : titleCase3(basename2(folder));
|
|
3914
|
+
const segment = typeof object.slug === "string" || object["skip-slug"] === true ? segmentFor(object, label, context) : fernBasicSlug(basename2(folder));
|
|
3915
|
+
const segments = segment ? [...parentSegments, segment] : parentSegments;
|
|
3916
|
+
const visit = (directory, routeSegments, depth) => {
|
|
3917
|
+
if (depth > 12 || context.descriptors.length >= 5e3) return [];
|
|
3918
|
+
const pages2 = [];
|
|
3919
|
+
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));
|
|
3920
|
+
for (const entry of entries) {
|
|
3921
|
+
const path = resolveWithin(directory, entry.name);
|
|
3922
|
+
if (entry.isDirectory()) {
|
|
3923
|
+
const nested = visit(path, [...routeSegments, fernBasicSlug(entry.name)], depth + 1);
|
|
3924
|
+
if (nested.length) pages2.push({ group: titleCase3(entry.name), pages: nested });
|
|
3925
|
+
} else if (entry.isFile() && [".md", ".mdx"].includes(extname4(entry.name).toLowerCase())) {
|
|
3926
|
+
const stem = basename2(entry.name, extname4(entry.name));
|
|
3927
|
+
const pageSegment = stem.toLowerCase() === "index" ? null : fernBasicSlug(stem);
|
|
3928
|
+
const route = [...routeSegments, ...pageSegment ? [pageSegment] : []].join("/");
|
|
3929
|
+
const source = lstatSync4(path).size <= MAX_CONFIG_BYTES2 ? readFileSync4(path, "utf8") : "";
|
|
3930
|
+
const frontmatter = source.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1] ?? "";
|
|
3931
|
+
const hidden = /^hidden:\s*true\s*$/m.test(frontmatter);
|
|
3932
|
+
const registered = registerPageAt(relative4(context.fernRoot, path), route, { ...context, pathPrefix: "" }, void 0, hidden);
|
|
3933
|
+
if (registered && !hidden && !context.hiddenInherited) pages2.push(registered);
|
|
3934
|
+
}
|
|
3935
|
+
}
|
|
3936
|
+
return pages2;
|
|
3937
|
+
};
|
|
3938
|
+
const pages = visit(folder, segments, 0);
|
|
3939
|
+
if (!pages.length) return null;
|
|
3940
|
+
addBareRouteRedirect(segments, pages, context);
|
|
3941
|
+
return { group: label, pages };
|
|
3942
|
+
}
|
|
3271
3943
|
function registerPage(object, parentSegments, context) {
|
|
3272
3944
|
if (typeof object.path !== "string" || !object.path.trim()) return null;
|
|
3273
3945
|
const label = typeof object.page === "string" && object.page.trim() ? object.page.trim() : "Untitled";
|
|
3274
3946
|
const segment = segmentFor(object, label, context);
|
|
3275
3947
|
const base = [...parentSegments, segment].filter(Boolean).join("/");
|
|
3276
|
-
return registerPageAt(object.path, base, context);
|
|
3948
|
+
return registerPageAt(object.path, base, context, label, object.hidden === true) || null;
|
|
3277
3949
|
}
|
|
3278
3950
|
function convertNode(node, parentSegments, context) {
|
|
3279
3951
|
const object = objectValue3(node);
|
|
3280
3952
|
if (!object) return null;
|
|
3281
|
-
if (typeof object.page === "string")
|
|
3953
|
+
if (typeof object.page === "string") {
|
|
3954
|
+
const page = registerPage(object, parentSegments, context);
|
|
3955
|
+
return object.hidden === true || context.hiddenInherited ? null : page;
|
|
3956
|
+
}
|
|
3957
|
+
if (typeof object.folder === "string") {
|
|
3958
|
+
const folder = registerFolder(object, parentSegments, object.hidden === true ? { ...context, hiddenInherited: true } : context);
|
|
3959
|
+
return object.hidden === true || context.hiddenInherited ? null : folder;
|
|
3960
|
+
}
|
|
3282
3961
|
if (typeof object.section === "string") {
|
|
3283
3962
|
const label = object.section;
|
|
3284
3963
|
const segment = segmentFor(object, label, context);
|
|
@@ -3287,14 +3966,16 @@ function convertNode(node, parentSegments, context) {
|
|
|
3287
3966
|
const pages = [];
|
|
3288
3967
|
const hasOwnPath = typeof object.path === "string" && object.path.trim().length > 0;
|
|
3289
3968
|
if (hasOwnPath) {
|
|
3290
|
-
|
|
3969
|
+
const registered = registerPageAt(object.path, segments.join("/"), context, label, object.hidden === true);
|
|
3970
|
+
if (registered) pages.push(registered);
|
|
3291
3971
|
}
|
|
3292
3972
|
for (const child of contents) {
|
|
3293
|
-
const converted = convertNode(child, segments, context);
|
|
3973
|
+
const converted = convertNode(child, segments, object.hidden === true ? { ...context, hiddenInherited: true } : context);
|
|
3294
3974
|
if (converted) pages.push(converted);
|
|
3295
3975
|
}
|
|
3296
3976
|
if (pages.length === 0) return null;
|
|
3297
3977
|
if (!hasOwnPath && segment) addBareRouteRedirect(segments, pages, context);
|
|
3978
|
+
if (object.hidden === true || context.hiddenInherited) return null;
|
|
3298
3979
|
return {
|
|
3299
3980
|
group: label,
|
|
3300
3981
|
...typeof object.icon === "string" ? { icon: object.icon } : {},
|
|
@@ -3319,8 +4000,7 @@ function convertNode(node, parentSegments, context) {
|
|
|
3319
4000
|
return null;
|
|
3320
4001
|
}
|
|
3321
4002
|
if (typeof object.changelog === "string") {
|
|
3322
|
-
|
|
3323
|
-
return null;
|
|
4003
|
+
return registerFolder({ folder: object.changelog, title: "Changelog", "skip-slug": true }, parentSegments, context);
|
|
3324
4004
|
}
|
|
3325
4005
|
return null;
|
|
3326
4006
|
}
|
|
@@ -3364,6 +4044,7 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
3364
4044
|
}
|
|
3365
4045
|
const versionConfig = objectValue3(readBoundedYaml(versionPath));
|
|
3366
4046
|
if (!versionConfig) return config;
|
|
4047
|
+
context.pathPrefix = relative4(fernRoot, dirname3(versionPath)).replace(/\\/g, "/");
|
|
3367
4048
|
return { ...config, navigation: versionConfig.navigation, tabs: versionConfig.tabs ?? config.tabs };
|
|
3368
4049
|
} catch (error) {
|
|
3369
4050
|
warnOnce2(
|
|
@@ -3392,6 +4073,43 @@ function buildTabsFromConfig(rawConfig, configDir, fernRoot, routePrefix, fallba
|
|
|
3392
4073
|
for (let index = sectionsBefore; index < context.apiSections.length; index += 1) {
|
|
3393
4074
|
context.apiSections[index].tabLabel ??= label;
|
|
3394
4075
|
}
|
|
4076
|
+
if (groups.length === 0 && typeof meta.href === "string" && /^https?:\/\//i.test(meta.href)) {
|
|
4077
|
+
return [{ tab: label, href: meta.href }];
|
|
4078
|
+
}
|
|
4079
|
+
if (groups.length === 0 && typeof meta.changelog === "string") {
|
|
4080
|
+
const changelog = registerFolder({ folder: meta.changelog, title: label, "skip-slug": true }, segments, context);
|
|
4081
|
+
if (changelog) {
|
|
4082
|
+
const indexRoute = segments.join("/");
|
|
4083
|
+
const descriptors = new Map(context.descriptors.map((descriptor) => [descriptor.navigationId, descriptor]));
|
|
4084
|
+
const visiblePages = changelog.pages.filter((page) => typeof page === "string").filter((page) => !descriptors.get(page)?.hidden);
|
|
4085
|
+
const index = visiblePages.find((page) => {
|
|
4086
|
+
const descriptor = descriptors.get(page);
|
|
4087
|
+
if (!descriptor) return false;
|
|
4088
|
+
const stem = basename2(descriptor.sourcePath, extname4(descriptor.sourcePath));
|
|
4089
|
+
return page === indexRoute || stem === "overview" || stem === "index";
|
|
4090
|
+
});
|
|
4091
|
+
const entries = visiblePages.filter((page) => page !== index).sort((left, right) => {
|
|
4092
|
+
const date = (page) => descriptors.get(page)?.sourcePath.match(/\d{4}-\d{2}-\d{2}/)?.[0] ?? "";
|
|
4093
|
+
return date(right).localeCompare(date(left)) || left.localeCompare(right);
|
|
4094
|
+
});
|
|
4095
|
+
for (const entryId of entries) {
|
|
4096
|
+
const descriptor = descriptors.get(entryId);
|
|
4097
|
+
if (descriptor) descriptor.hidden = true;
|
|
4098
|
+
}
|
|
4099
|
+
changelog.pages = [index ?? indexRoute];
|
|
4100
|
+
context.changelogIndexes.push({ route: indexRoute, entries });
|
|
4101
|
+
const latest = index ?? firstLeafId(changelog.pages);
|
|
4102
|
+
const bareRouteIndex = context.bareRouteRedirects.findIndex((redirect) => redirect.source === `/${indexRoute}`);
|
|
4103
|
+
const indexDescriptor = index ? descriptors.get(index) : void 0;
|
|
4104
|
+
const indexSource = indexDescriptor && lstatSync4(resolveWithinRoot(context.fernRoot, indexDescriptor.sourcePath, context.repositoryRoot)).size <= MAX_CONFIG_BYTES2 ? readFileSync4(resolveWithinRoot(context.fernRoot, indexDescriptor.sourcePath, context.repositoryRoot), "utf8") : "";
|
|
4105
|
+
const indexFrontmatter = indexSource.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/)?.[1] ?? "";
|
|
4106
|
+
const indexSlug = indexFrontmatter.match(/^slug:\s*["']?([^\s"']+)["']?\s*$/m)?.[1]?.replace(/^\/+|\/+$/g, "");
|
|
4107
|
+
const hasBarePage = index === indexRoute || indexSlug === indexRoute;
|
|
4108
|
+
if (bareRouteIndex >= 0 && hasBarePage) context.bareRouteRedirects.splice(bareRouteIndex, 1);
|
|
4109
|
+
else if (latest && bareRouteIndex >= 0) context.bareRouteRedirects[bareRouteIndex].destination = `/${latest}`;
|
|
4110
|
+
return [{ tab: label, groups: [changelog] }];
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
3395
4113
|
if (groups.length === 0) return [];
|
|
3396
4114
|
if (segments.length > 0) addBareRouteRedirect(segments, groups, context);
|
|
3397
4115
|
return [{
|
|
@@ -3463,9 +4181,12 @@ function projectFernProducts(rawProducts, fernRoot, context) {
|
|
|
3463
4181
|
}
|
|
3464
4182
|
function projectFernNavigation(input) {
|
|
3465
4183
|
const context = {
|
|
4184
|
+
fernRoot: input.fernRoot,
|
|
4185
|
+
repositoryRoot: input.repositoryRoot ?? input.fernRoot,
|
|
3466
4186
|
descriptors: [],
|
|
3467
4187
|
seenNavigationIds: /* @__PURE__ */ new Set(),
|
|
3468
4188
|
apiSections: [],
|
|
4189
|
+
changelogIndexes: [],
|
|
3469
4190
|
warnings: [],
|
|
3470
4191
|
warningKeys: /* @__PURE__ */ new Set(),
|
|
3471
4192
|
bareRouteRedirects: [],
|
|
@@ -3488,6 +4209,9 @@ function projectFernNavigation(input) {
|
|
|
3488
4209
|
const label = typeof link?.text === "string" ? link.text : typeof link?.label === "string" ? link.label : link?.type === "github" ? "GitHub" : void 0;
|
|
3489
4210
|
return href && label ? [{ label, href, ...link?.type === "github" ? { type: "github" } : {} }] : [];
|
|
3490
4211
|
}) : [];
|
|
4212
|
+
const announcement = objectValue3(config.announcement);
|
|
4213
|
+
const announcementMessage = typeof announcement?.message === "string" ? announcement.message.trim() : "";
|
|
4214
|
+
const bannerContent = announcementMarkdown(announcementMessage);
|
|
3491
4215
|
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
3492
4216
|
const redirect = objectValue3(value);
|
|
3493
4217
|
if (!redirect || typeof redirect.source !== "string" || typeof redirect.destination !== "string") return [];
|
|
@@ -3509,13 +4233,6 @@ function projectFernNavigation(input) {
|
|
|
3509
4233
|
...typeof redirect.permanent === "boolean" ? { permanent: redirect.permanent } : {}
|
|
3510
4234
|
}];
|
|
3511
4235
|
}) : [];
|
|
3512
|
-
if (config.logo || config.favicon) {
|
|
3513
|
-
warnOnce2(
|
|
3514
|
-
context,
|
|
3515
|
-
"fern-branding",
|
|
3516
|
-
"Fern logo and favicon were not migrated; add them to public/brand/ (default-logo-light.svg, default-logo-dark.svg, default-favicon-light.svg, default-favicon-dark.svg)."
|
|
3517
|
-
);
|
|
3518
|
-
}
|
|
3519
4236
|
const authoredSources = new Set(redirects.map((redirect) => redirect.source));
|
|
3520
4237
|
const knownIds = new Set(context.descriptors.map((descriptor) => descriptor.navigationId));
|
|
3521
4238
|
const keptNavigationId = (descriptor) => context.sourcePathToNavigationId.get(descriptor.sourcePath) ?? descriptor.navigationId;
|
|
@@ -3559,12 +4276,14 @@ function projectFernNavigation(input) {
|
|
|
3559
4276
|
return {
|
|
3560
4277
|
docsConfig: {
|
|
3561
4278
|
tabs,
|
|
4279
|
+
...bannerContent ? { banner: { content: bannerContent, dismissible: true } } : {},
|
|
3562
4280
|
...navbarLinks.length > 0 ? { navbar: { links: navbarLinks } } : {},
|
|
3563
4281
|
...allRedirects.length > 0 ? { redirects: allRedirects } : {}
|
|
3564
4282
|
},
|
|
3565
4283
|
descriptors: context.descriptors,
|
|
3566
4284
|
warnings: context.warnings,
|
|
3567
|
-
apiSections: disambiguatedApiSections
|
|
4285
|
+
apiSections: disambiguatedApiSections,
|
|
4286
|
+
changelogIndexes: context.changelogIndexes
|
|
3568
4287
|
};
|
|
3569
4288
|
}
|
|
3570
4289
|
|
|
@@ -3618,7 +4337,8 @@ var REPOSITORY_ONLY_DOCUMENTS = /* @__PURE__ */ new Set([
|
|
|
3618
4337
|
"security.md"
|
|
3619
4338
|
]);
|
|
3620
4339
|
var SNIPPET_DIRECTORIES = /* @__PURE__ */ new Set(["snippets", "_snippets", "partials", "_partials"]);
|
|
3621
|
-
var SNIPPET_IMPORT_PATTERN =
|
|
4340
|
+
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;
|
|
4341
|
+
var SNIPPET_VALUE_IMPORT_PATTERN = /\bimport\s*\{([^{}]+)\}\s*from\s+['"]([^'"]+\.mdx?)['"]\s*;?/g;
|
|
3622
4342
|
var SNIPPET_TAG_PATTERN = /<Snippet\s+file=(?:"([^"]+)"|'([^']+)')\s*(?:\/>|>[\s\S]*?<\/Snippet>)/g;
|
|
3623
4343
|
var MINTIGNORE_FILENAME = ".mintignore";
|
|
3624
4344
|
var createIgnoreMatcher = createRequire(import.meta.url)("ignore");
|
|
@@ -3664,7 +4384,7 @@ function countMarkdownPages(directory, limit = 2e4) {
|
|
|
3664
4384
|
const current = stack.pop();
|
|
3665
4385
|
let entries;
|
|
3666
4386
|
try {
|
|
3667
|
-
entries =
|
|
4387
|
+
entries = readdirSync2(current, { withFileTypes: true });
|
|
3668
4388
|
} catch {
|
|
3669
4389
|
continue;
|
|
3670
4390
|
}
|
|
@@ -3683,7 +4403,7 @@ function pickPreferredDocsRoot(candidates, repositoryDir, platformLabel, warning
|
|
|
3683
4403
|
const ranked = [...candidates].sort((left, right) => {
|
|
3684
4404
|
const byRank = (rank?.(right) ?? 0) - (rank?.(left) ?? 0);
|
|
3685
4405
|
if (byRank !== 0) return byRank;
|
|
3686
|
-
const byName = (
|
|
4406
|
+
const byName = (basename3(right) === "docs" ? 1 : 0) - (basename3(left) === "docs" ? 1 : 0);
|
|
3687
4407
|
if (byName !== 0) return byName;
|
|
3688
4408
|
return countMarkdownPages(right) - countMarkdownPages(left);
|
|
3689
4409
|
});
|
|
@@ -3717,7 +4437,7 @@ function findMintlifyProjectRoot(repositoryDir, docsDir, warnings) {
|
|
|
3717
4437
|
visited++;
|
|
3718
4438
|
if (hasMintlifyConfig(current.directory)) candidates.push(current.directory);
|
|
3719
4439
|
if (current.depth >= 4) continue;
|
|
3720
|
-
for (const entry of
|
|
4440
|
+
for (const entry of readdirSync2(current.directory, { withFileTypes: true })) {
|
|
3721
4441
|
if (!entry.isDirectory() || entry.isSymbolicLink() || isIgnoredDirectory(entry.name)) continue;
|
|
3722
4442
|
queue.push({ directory: resolveWithin(current.directory, entry.name), depth: current.depth + 1 });
|
|
3723
4443
|
}
|
|
@@ -3749,7 +4469,7 @@ function findFernProjectRoot(repositoryDir, docsDir) {
|
|
|
3749
4469
|
visited++;
|
|
3750
4470
|
if (hasFernConfig(current.directory)) return current.directory;
|
|
3751
4471
|
if (current.depth >= 4) continue;
|
|
3752
|
-
for (const entry of
|
|
4472
|
+
for (const entry of readdirSync2(current.directory, { withFileTypes: true })) {
|
|
3753
4473
|
if (!entry.isDirectory() || entry.isSymbolicLink() || isIgnoredDirectory(entry.name)) continue;
|
|
3754
4474
|
queue.push({ directory: resolveWithin(current.directory, entry.name), depth: current.depth + 1 });
|
|
3755
4475
|
}
|
|
@@ -3775,7 +4495,7 @@ function findDocusaurusProjectRoot(repositoryDir, docsDir, warnings) {
|
|
|
3775
4495
|
visited++;
|
|
3776
4496
|
if (hasDocusaurusConfig(current.directory)) candidates.push(current.directory);
|
|
3777
4497
|
if (current.depth >= 4) continue;
|
|
3778
|
-
for (const entry of
|
|
4498
|
+
for (const entry of readdirSync2(current.directory, { withFileTypes: true })) {
|
|
3779
4499
|
if (!entry.isDirectory() || entry.isSymbolicLink() || isIgnoredDirectory(entry.name)) continue;
|
|
3780
4500
|
queue.push({
|
|
3781
4501
|
directory: resolveWithin(current.directory, entry.name),
|
|
@@ -3796,6 +4516,52 @@ function primaryDocusaurusDocsDirectory(projectRoot) {
|
|
|
3796
4516
|
const configured = source.match(/\bdocs\s*:\s*\{[\s\S]{0,4000}?\bpath\s*:\s*(['"])([^'"]+)\1/)?.[2];
|
|
3797
4517
|
return trimTrailingSlashes(configured?.replace(/^\.\//, "") ?? "") || "docs";
|
|
3798
4518
|
}
|
|
4519
|
+
function docusaurusArchiveRoots(projectRoot, repositoryDir, warnings) {
|
|
4520
|
+
const archives = [];
|
|
4521
|
+
const relativeRoot = relative5(repositoryDir, projectRoot).replace(/\\/g, "/");
|
|
4522
|
+
const directoryNames = (directory) => {
|
|
4523
|
+
const path = resolveWithin(projectRoot, directory);
|
|
4524
|
+
if (!existsSync5(path) || !lstatSync5(path).isDirectory()) return [];
|
|
4525
|
+
return readdirSync2(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.isSymbolicLink()).map((entry) => entry.name).sort();
|
|
4526
|
+
};
|
|
4527
|
+
let versions = directoryNames("versioned_docs").filter((name) => /^version-[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name));
|
|
4528
|
+
const versionsPath = resolveWithin(projectRoot, "versions.json");
|
|
4529
|
+
if (existsSync5(versionsPath) && lstatSync5(versionsPath).isFile()) {
|
|
4530
|
+
try {
|
|
4531
|
+
if (lstatSync5(versionsPath).size > 1e5) throw new Error("file exceeds the 100 KB limit");
|
|
4532
|
+
const declared = JSON.parse(readFileSync5(versionsPath, "utf8"));
|
|
4533
|
+
if (!Array.isArray(declared) || !declared.every((value) => typeof value === "string")) throw new Error("expected an array of version names");
|
|
4534
|
+
const declaredOrder = declared.map((value) => `version-${value}`);
|
|
4535
|
+
versions = [...declaredOrder.filter((name) => versions.includes(name)), ...versions.filter((name) => !declaredOrder.includes(name))];
|
|
4536
|
+
} catch (error) {
|
|
4537
|
+
warnings.push({ code: "unsupported-config", message: `Docusaurus versions.json could not be read safely: ${error instanceof Error ? error.message : String(error)}` });
|
|
4538
|
+
}
|
|
4539
|
+
}
|
|
4540
|
+
const sidebarPath = (version) => {
|
|
4541
|
+
const candidates = ["json", "js", "cjs", "mjs", "ts"].map((extension) => `versioned_sidebars/${version}-sidebars.${extension}`);
|
|
4542
|
+
return candidates.find((candidate) => {
|
|
4543
|
+
const path = resolveWithin(projectRoot, candidate);
|
|
4544
|
+
return existsSync5(path) && lstatSync5(path).isFile();
|
|
4545
|
+
});
|
|
4546
|
+
};
|
|
4547
|
+
const add = (directory, routePrefix, label, sidebar) => {
|
|
4548
|
+
const docsDir = [relativeRoot, directory].filter(Boolean).join("/");
|
|
4549
|
+
archives.push({ docsDir, routePrefix, label, ...sidebar ? { sidebarPath: sidebar } : {} });
|
|
4550
|
+
};
|
|
4551
|
+
for (const version of versions) add(`versioned_docs/${version}`, version.slice("version-".length), `Version ${version.slice("version-".length)}`, sidebarPath(version));
|
|
4552
|
+
for (const locale of directoryNames("i18n").filter((name) => /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{2,8})*$/.test(name))) {
|
|
4553
|
+
const localizedRoot = `i18n/${locale}/docusaurus-plugin-content-docs`;
|
|
4554
|
+
const current = resolveWithin(projectRoot, `${localizedRoot}/current`);
|
|
4555
|
+
if (existsSync5(current) && lstatSync5(current).isDirectory()) add(`${localizedRoot}/current`, locale, `${locale} Documentation`);
|
|
4556
|
+
const localizedVersions = directoryNames(localizedRoot).filter((name) => /^version-[A-Za-z0-9][A-Za-z0-9._-]*$/.test(name));
|
|
4557
|
+
for (const version of localizedVersions) {
|
|
4558
|
+
const path = resolveWithin(projectRoot, `${localizedRoot}/${version}`);
|
|
4559
|
+
if (existsSync5(path) && lstatSync5(path).isDirectory()) add(`${localizedRoot}/${version}`, `${locale}/${version.slice("version-".length)}`, `${locale} Version ${version.slice("version-".length)}`);
|
|
4560
|
+
}
|
|
4561
|
+
}
|
|
4562
|
+
if (archives.length > 48) warnings.push({ code: "limit-reached", message: `Only the first 48 Docusaurus archived or localized docs roots were imported (${archives.length} found).` });
|
|
4563
|
+
return archives.slice(0, 48);
|
|
4564
|
+
}
|
|
3799
4565
|
function additionalDocusaurusPluginRoots(repositoryDir, projectRoot, warnings) {
|
|
3800
4566
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
3801
4567
|
const plugins = [];
|
|
@@ -4022,10 +4788,10 @@ function detectRepositoryPlatform(repositoryDir, docsDir) {
|
|
|
4022
4788
|
}
|
|
4023
4789
|
function containsMarkdown(directory, depth = 0) {
|
|
4024
4790
|
if (depth > 4) return false;
|
|
4025
|
-
for (const entry of
|
|
4791
|
+
for (const entry of readdirSync2(directory, { withFileTypes: true })) {
|
|
4026
4792
|
if (isIgnoredDirectory(entry.name) || entry.isSymbolicLink()) continue;
|
|
4027
4793
|
const path = resolveWithin(directory, entry.name);
|
|
4028
|
-
if (entry.isFile() && [".md", ".mdx"].includes(
|
|
4794
|
+
if (entry.isFile() && [".md", ".mdx"].includes(extname5(entry.name).toLowerCase())) return true;
|
|
4029
4795
|
if (entry.isDirectory() && containsMarkdown(path, depth + 1)) return true;
|
|
4030
4796
|
}
|
|
4031
4797
|
return false;
|
|
@@ -4058,7 +4824,7 @@ function scanFiles(root, confinementRoot = root, warnings) {
|
|
|
4058
4824
|
visitedRealPaths.add(directoryReal);
|
|
4059
4825
|
let entries;
|
|
4060
4826
|
try {
|
|
4061
|
-
entries =
|
|
4827
|
+
entries = readdirSync2(directory, { withFileTypes: true });
|
|
4062
4828
|
} catch {
|
|
4063
4829
|
return;
|
|
4064
4830
|
}
|
|
@@ -4138,12 +4904,6 @@ function fernThemeColors(value) {
|
|
|
4138
4904
|
if (lightMode) colors.dark = lightMode;
|
|
4139
4905
|
return Object.keys(colors).length > 0 ? colors : void 0;
|
|
4140
4906
|
}
|
|
4141
|
-
function mintlifyBrandAssetPaths(value) {
|
|
4142
|
-
if (typeof value === "string") return [value];
|
|
4143
|
-
if (!value || typeof value !== "object") return [];
|
|
4144
|
-
const source = value;
|
|
4145
|
-
return ["light", "dark"].map((key) => source[key]).filter((entry) => typeof entry === "string");
|
|
4146
|
-
}
|
|
4147
4907
|
function normalizedReferenceKey(value) {
|
|
4148
4908
|
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "").replace(/\/(?:index|readme)$/i, "").replace(/^(?:index|readme)$/i, "") || "introduction";
|
|
4149
4909
|
}
|
|
@@ -4151,7 +4911,25 @@ function exactReferenceKey(value) {
|
|
|
4151
4911
|
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
4152
4912
|
}
|
|
4153
4913
|
function findOpenApi(files) {
|
|
4154
|
-
return files.find((file) => OPENAPI_FILENAMES.has(
|
|
4914
|
+
return files.find((file) => OPENAPI_FILENAMES.has(basename3(file.relativePath).toLowerCase()) && classifyApiSpec(file.absolutePath) === "openapi") ?? null;
|
|
4915
|
+
}
|
|
4916
|
+
function classifyApiSpec(path) {
|
|
4917
|
+
const buffer = Buffer.alloc(64e3);
|
|
4918
|
+
let bytesRead = 0;
|
|
4919
|
+
try {
|
|
4920
|
+
const descriptor = openSync(path, "r");
|
|
4921
|
+
try {
|
|
4922
|
+
bytesRead = readSync(descriptor, buffer, 0, buffer.length, 0);
|
|
4923
|
+
} finally {
|
|
4924
|
+
closeSync(descriptor);
|
|
4925
|
+
}
|
|
4926
|
+
} catch {
|
|
4927
|
+
return "unknown";
|
|
4928
|
+
}
|
|
4929
|
+
const text = buffer.toString("utf8", 0, bytesRead);
|
|
4930
|
+
const topLevel = text.match(/^(?:openapi|swagger|asyncapi)\s*:/m)?.[0] ?? text.match(/^\s*\{\s*"(?:openapi|swagger|asyncapi)"\s*:/m)?.[0];
|
|
4931
|
+
if (!topLevel) return "unknown";
|
|
4932
|
+
return /asyncapi/i.test(topLevel) ? "asyncapi" : "openapi";
|
|
4155
4933
|
}
|
|
4156
4934
|
function mintlifyTopLevelApiReferences(config) {
|
|
4157
4935
|
const api = config?.api && typeof config.api === "object" && !Array.isArray(config.api) ? config.api : null;
|
|
@@ -4200,7 +4978,7 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings) {
|
|
|
4200
4978
|
});
|
|
4201
4979
|
continue;
|
|
4202
4980
|
}
|
|
4203
|
-
specs.push({ filename:
|
|
4981
|
+
specs.push({ filename: basename3(match.relativePath), content: readFileSync5(match.absolutePath), tabLabel: reference.tabLabel });
|
|
4204
4982
|
}
|
|
4205
4983
|
return specs;
|
|
4206
4984
|
}
|
|
@@ -4267,6 +5045,12 @@ function findFernConfiguredOpenApi(fernRoot, repositoryDir, apiName, apiNameExpl
|
|
|
4267
5045
|
try {
|
|
4268
5046
|
const absolute = resolveWithinRoot(dir, specPath, repositoryDir);
|
|
4269
5047
|
if (!existsSync5(absolute) || !lstatSync5(absolute).isFile()) continue;
|
|
5048
|
+
const kind = classifyApiSpec(absolute);
|
|
5049
|
+
if (kind === "asyncapi") {
|
|
5050
|
+
unsupported.push({ kind: "asyncapi", path: specPath });
|
|
5051
|
+
continue;
|
|
5052
|
+
}
|
|
5053
|
+
if (kind !== "openapi") continue;
|
|
4270
5054
|
return { spec: { absolutePath: absolute, relativePath: relative5(repositoryDir, absolute).replace(/\\/g, "/") }, unsupported };
|
|
4271
5055
|
} catch {
|
|
4272
5056
|
warnings.push({
|
|
@@ -4304,9 +5088,11 @@ function staticSnippetProperties(attributes) {
|
|
|
4304
5088
|
}
|
|
4305
5089
|
return properties;
|
|
4306
5090
|
}
|
|
4307
|
-
function interpolateSnippet(snippet, attributes) {
|
|
5091
|
+
function interpolateSnippet(snippet, attributes, children) {
|
|
4308
5092
|
const properties = staticSnippetProperties(attributes);
|
|
5093
|
+
if (children !== void 0) properties.set("children", children.trim());
|
|
4309
5094
|
if (properties.size === 0) return snippet;
|
|
5095
|
+
const isJsxSnippet = /<[A-Za-z][A-Za-z0-9]*/.test(snippet);
|
|
4310
5096
|
let fence = "";
|
|
4311
5097
|
return snippet.split("\n").map((line) => {
|
|
4312
5098
|
const marker = line.match(/^\s*(`{3,}|~{3,})/)?.[1];
|
|
@@ -4317,11 +5103,276 @@ function interpolateSnippet(snippet, attributes) {
|
|
|
4317
5103
|
}
|
|
4318
5104
|
if (fence) return line;
|
|
4319
5105
|
return line.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (original, name) => {
|
|
4320
|
-
|
|
5106
|
+
const value = properties.get(name);
|
|
5107
|
+
if (value === void 0) return original;
|
|
5108
|
+
if (name === "children") return value;
|
|
5109
|
+
return isJsxSnippet ? `{${JSON.stringify(value)}}` : value;
|
|
5110
|
+
});
|
|
5111
|
+
}).join("\n");
|
|
5112
|
+
}
|
|
5113
|
+
function snippetComponentBody(source, componentName) {
|
|
5114
|
+
const parsed = ts2.createSourceFile("snippet.tsx", source, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
|
|
5115
|
+
for (const statement of parsed.statements) {
|
|
5116
|
+
if (!ts2.isVariableStatement(statement)) continue;
|
|
5117
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
5118
|
+
if (!ts2.isIdentifier(declaration.name) || declaration.name.text !== componentName) continue;
|
|
5119
|
+
const initializer = declaration.initializer;
|
|
5120
|
+
if (!initializer || !ts2.isArrowFunction(initializer)) continue;
|
|
5121
|
+
const returned = ts2.isBlock(initializer.body) ? initializer.body.statements.find(ts2.isReturnStatement)?.expression : initializer.body;
|
|
5122
|
+
if (!returned) continue;
|
|
5123
|
+
let expression = returned;
|
|
5124
|
+
while (ts2.isParenthesizedExpression(expression)) expression = expression.expression;
|
|
5125
|
+
if (ts2.isJsxElement(expression) || ts2.isJsxSelfClosingElement(expression) || ts2.isJsxFragment(expression)) {
|
|
5126
|
+
return expression.getText(parsed);
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
return source;
|
|
5131
|
+
}
|
|
5132
|
+
function statefulSnippetDeclaration(source, componentName) {
|
|
5133
|
+
const parsed = ts2.createSourceFile("snippet.tsx", source, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
|
|
5134
|
+
if (parsed.statements.length !== 1) return null;
|
|
5135
|
+
const statement = parsed.statements[0];
|
|
5136
|
+
if (!ts2.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts2.SyntaxKind.ExportKeyword) || statement.declarationList.declarations.length !== 1) return null;
|
|
5137
|
+
const declaration = statement.declarationList.declarations[0];
|
|
5138
|
+
if (!ts2.isIdentifier(declaration.name) || declaration.name.text !== componentName || !declaration.initializer || !ts2.isArrowFunction(declaration.initializer) || !ts2.isBlock(declaration.initializer.body)) return null;
|
|
5139
|
+
const body = declaration.initializer.body.statements;
|
|
5140
|
+
if (body.length < 2 || !body.some(ts2.isReturnStatement)) return null;
|
|
5141
|
+
return source.trim();
|
|
5142
|
+
}
|
|
5143
|
+
function staticNamedSnippetValues(source) {
|
|
5144
|
+
const values = /* @__PURE__ */ new Map();
|
|
5145
|
+
const parsed = ts2.createSourceFile("snippet.tsx", source, ts2.ScriptTarget.Latest, true, ts2.ScriptKind.TSX);
|
|
5146
|
+
for (const statement of parsed.statements) {
|
|
5147
|
+
if (!ts2.isVariableStatement(statement) || !statement.modifiers?.some((modifier) => modifier.kind === ts2.SyntaxKind.ExportKeyword)) continue;
|
|
5148
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
5149
|
+
if (!ts2.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
5150
|
+
const expression = declaration.initializer;
|
|
5151
|
+
if (ts2.isStringLiteral(expression) || ts2.isNoSubstitutionTemplateLiteral(expression)) {
|
|
5152
|
+
values.set(declaration.name.text, JSON.stringify(expression.text));
|
|
5153
|
+
} else if (ts2.isNumericLiteral(expression)) {
|
|
5154
|
+
values.set(declaration.name.text, expression.text);
|
|
5155
|
+
} else if (expression.kind === ts2.SyntaxKind.TrueKeyword || expression.kind === ts2.SyntaxKind.FalseKeyword) {
|
|
5156
|
+
values.set(declaration.name.text, expression.kind === ts2.SyntaxKind.TrueKeyword ? "true" : "false");
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5159
|
+
}
|
|
5160
|
+
return values;
|
|
5161
|
+
}
|
|
5162
|
+
function hoistMdxImports(source) {
|
|
5163
|
+
const imports2 = /* @__PURE__ */ new Set();
|
|
5164
|
+
const withoutImports = replaceOutsideCode(source, (body) => body.replace(
|
|
5165
|
+
/^[ \t]*import\s+(?:\{[^}\n]+\}|[A-Za-z_$][\w$]*)\s+from\s+(['"])[^'"\n]+\1[ \t]*;?/gm,
|
|
5166
|
+
(statement) => {
|
|
5167
|
+
imports2.add(statement.trim().replace(/;?$/, ";"));
|
|
5168
|
+
return "";
|
|
5169
|
+
}
|
|
5170
|
+
));
|
|
5171
|
+
if (imports2.size === 0) return source;
|
|
5172
|
+
const frontmatter = withoutImports.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
5173
|
+
const prefix = frontmatter?.[0] ?? "";
|
|
5174
|
+
return `${prefix}${[...imports2].join("\n")}
|
|
5175
|
+
|
|
5176
|
+
${withoutImports.slice(prefix.length).replace(/^(?:\r?\n)*/, "")}`;
|
|
5177
|
+
}
|
|
5178
|
+
function fernSourceBasePath(config) {
|
|
5179
|
+
const instance = Array.isArray(config.instances) ? config.instances[0] : null;
|
|
5180
|
+
if (!instance || typeof instance !== "object" || Array.isArray(instance)) return "";
|
|
5181
|
+
const value = instance;
|
|
5182
|
+
const address = typeof value["custom-domain"] === "string" ? value["custom-domain"] : value.url;
|
|
5183
|
+
if (typeof address !== "string") return "";
|
|
5184
|
+
try {
|
|
5185
|
+
const path = new URL(/^https?:\/\//i.test(address) ? address : `https://${address}`).pathname.replace(/\/+$/, "");
|
|
5186
|
+
return path === "/" ? "" : path;
|
|
5187
|
+
} catch {
|
|
5188
|
+
return "";
|
|
5189
|
+
}
|
|
5190
|
+
}
|
|
5191
|
+
function fernDefaultVersionPath(config) {
|
|
5192
|
+
if (!Array.isArray(config.versions)) return "";
|
|
5193
|
+
const versions = config.versions.filter((entry) => !!entry && typeof entry === "object" && !Array.isArray(entry));
|
|
5194
|
+
const chosen = versions.find((version) => version.default === true) ?? versions[0];
|
|
5195
|
+
return typeof chosen?.slug === "string" && /^[A-Za-z0-9_-]+$/.test(chosen.slug) ? `/${chosen.slug}` : "";
|
|
5196
|
+
}
|
|
5197
|
+
function fernSourceSiteUrl(config) {
|
|
5198
|
+
const instance = Array.isArray(config.instances) ? config.instances[0] : null;
|
|
5199
|
+
if (!instance || typeof instance !== "object" || Array.isArray(instance)) return void 0;
|
|
5200
|
+
const value = instance;
|
|
5201
|
+
const address = typeof value["custom-domain"] === "string" ? value["custom-domain"] : value.url;
|
|
5202
|
+
if (typeof address !== "string") return void 0;
|
|
5203
|
+
try {
|
|
5204
|
+
const url = new URL(/^https?:\/\//i.test(address) ? address : `https://${address}`);
|
|
5205
|
+
return `${url.origin}${fernSourceBasePath(config)}${fernDefaultVersionPath(config)}`;
|
|
5206
|
+
} catch {
|
|
5207
|
+
return void 0;
|
|
5208
|
+
}
|
|
5209
|
+
}
|
|
5210
|
+
function externalizeMissingFernLinks(body, pageIds, sourceSiteUrl, externalized) {
|
|
5211
|
+
const rewrite = (target) => {
|
|
5212
|
+
const path = target.split(/[?#]/, 1)[0].replace(/^\//, "").replace(/\/$/, "");
|
|
5213
|
+
if (!path || pageIds.has(path) || ASSET_EXTENSIONS.has(extname5(path).toLowerCase())) return target;
|
|
5214
|
+
try {
|
|
5215
|
+
const base = new URL(`${sourceSiteUrl}/`);
|
|
5216
|
+
const sourceLinkUrl = new URL(target.slice(1), base);
|
|
5217
|
+
if (sourceLinkUrl.origin !== base.origin) return target;
|
|
5218
|
+
const sourceLink = sourceLinkUrl.toString();
|
|
5219
|
+
externalized.add(target);
|
|
5220
|
+
return sourceLink;
|
|
5221
|
+
} catch {
|
|
5222
|
+
return target;
|
|
5223
|
+
}
|
|
5224
|
+
};
|
|
5225
|
+
return replaceOutsideCode(body, (text) => text.replace(/(\]\()\/(?!\/)([^\s)]+)(\))/g, (_match, before, target, after) => `${before}${rewrite(`/${target}`)}${after}`).replace(/(\b(?:href|to)=(['"]))\/(?!\/)([^'"\n]+)\2/g, (_match, before, quote, target) => `${before}${rewrite(`/${target}`)}${quote}`));
|
|
5226
|
+
}
|
|
5227
|
+
function stripFernBasePathFromLinks(body, basePath) {
|
|
5228
|
+
if (!basePath) return body;
|
|
5229
|
+
const escaped = basePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5230
|
+
return replaceOutsideCode(body, (text) => text.replace(new RegExp(`(\\]\\()${escaped}(?=\\/|[)#?])`, "g"), (match, prefix, offset, source) => prefix + (source[offset + match.length] === "/" ? "" : "/")).replace(new RegExp(`(\\b(?:href|to)=["'])${escaped}(?=\\/|["'#?])`, "g"), (match, prefix, offset, source) => prefix + (source[offset + match.length] === "/" ? "" : "/")));
|
|
5231
|
+
}
|
|
5232
|
+
function rewriteMintlifyMountedLinks(body, pageIds) {
|
|
5233
|
+
const rewrite = (target) => {
|
|
5234
|
+
const suffixAt = target.search(/[?#]/);
|
|
5235
|
+
const pathname = suffixAt < 0 ? target : target.slice(0, suffixAt);
|
|
5236
|
+
const suffix = suffixAt < 0 ? "" : target.slice(suffixAt);
|
|
5237
|
+
if (!pathname.startsWith("/") || pathname.startsWith("//") || pathname.includes("\\")) return target;
|
|
5238
|
+
const segments = pathname.slice(1).split("/");
|
|
5239
|
+
if (segments.some((segment) => !segment || segment === "." || segment === "..")) return target;
|
|
5240
|
+
if (pageIds.has(segments.join("/"))) return target;
|
|
5241
|
+
for (let count = 1; count < segments.length && count <= 3; count++) {
|
|
5242
|
+
const candidate = segments.slice(count).join("/");
|
|
5243
|
+
if (pageIds.has(candidate)) return `/${candidate}${suffix}`;
|
|
5244
|
+
}
|
|
5245
|
+
return target;
|
|
5246
|
+
};
|
|
5247
|
+
return replaceOutsideCode(body, (text) => text.replace(/(\]\()(\/[^\s)]+)(\))/g, (_match, before, target, after) => `${before}${rewrite(target)}${after}`).replace(/(\b(?:href|to)=(['"]))(\/[^'"\n]+)\2/g, (_match, before, quote, target) => `${before}${rewrite(target)}${quote}`));
|
|
5248
|
+
}
|
|
5249
|
+
function preserveLinkedAnchors(pages) {
|
|
5250
|
+
const pageById = new Map(pages.map((page) => [page.id, page]));
|
|
5251
|
+
const requested = /* @__PURE__ */ new Map();
|
|
5252
|
+
const crossPageRequests = /* @__PURE__ */ new Set();
|
|
5253
|
+
const addTarget = (target, currentId) => {
|
|
5254
|
+
const hash2 = target.indexOf("#");
|
|
5255
|
+
if (hash2 < 0) return;
|
|
5256
|
+
let fragment;
|
|
5257
|
+
try {
|
|
5258
|
+
fragment = decodeURIComponent(target.slice(hash2 + 1));
|
|
5259
|
+
} catch {
|
|
5260
|
+
return;
|
|
5261
|
+
}
|
|
5262
|
+
if (!/^[\p{L}\p{N}_.:$-]{1,120}$/u.test(fragment)) return;
|
|
5263
|
+
const path = target.slice(0, hash2).split("?")[0];
|
|
5264
|
+
const id = target.startsWith("#") ? currentId : path.startsWith("/") && !path.startsWith("//") ? path.replace(/^\//, "").replace(/\/$/, "") || "introduction" : "";
|
|
5265
|
+
if (!pageById.has(id)) return;
|
|
5266
|
+
if (!requested.has(id)) requested.set(id, /* @__PURE__ */ new Set());
|
|
5267
|
+
requested.get(id).add(fragment);
|
|
5268
|
+
if (id !== currentId) crossPageRequests.add(`${id}#${fragment}`);
|
|
5269
|
+
};
|
|
5270
|
+
for (const page of pages) {
|
|
5271
|
+
replaceOutsideCode(page.body, (text) => {
|
|
5272
|
+
for (const match of text.matchAll(/\]\(([^)\s]+)\)/g)) addTarget(match[1], page.id);
|
|
5273
|
+
for (const match of text.matchAll(/\b(?:href|to)=(['"])([^'"\n]+)\1/g)) addTarget(match[2], page.id);
|
|
5274
|
+
return text;
|
|
4321
5275
|
});
|
|
5276
|
+
}
|
|
5277
|
+
const comparable = (value) => value.replace(/&(?:lt|gt|amp|quot);/g, " ").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "");
|
|
5278
|
+
for (const page of pages) {
|
|
5279
|
+
const fragments = requested.get(page.id);
|
|
5280
|
+
if (!fragments?.size) continue;
|
|
5281
|
+
const lines = page.body.split("\n");
|
|
5282
|
+
let fence;
|
|
5283
|
+
const tableCells = [];
|
|
5284
|
+
const headings = lines.map((line, index) => {
|
|
5285
|
+
const marker = line.match(/^ {0,3}(`{3,}|~{3,})/);
|
|
5286
|
+
if (marker) {
|
|
5287
|
+
if (!fence) fence = marker[1];
|
|
5288
|
+
else if (marker[1][0] === fence[0] && marker[1].length >= fence.length && /^\s*$/.test(line.slice(marker[0].length))) fence = void 0;
|
|
5289
|
+
return null;
|
|
5290
|
+
}
|
|
5291
|
+
if (fence) return null;
|
|
5292
|
+
const tableCell = line.match(/^ {0,3}\|\s*([^|]+?)\s*\|/);
|
|
5293
|
+
if (tableCell && !/^[:\s-]+$/.test(tableCell[1])) tableCells.push({ index, text: tableCell[1] });
|
|
5294
|
+
const match = line.match(/^ {0,3}#{1,6}\s+(.+?)\s*#*\s*$/);
|
|
5295
|
+
return match ? { index, text: match[1] } : null;
|
|
5296
|
+
}).filter((entry) => entry !== null);
|
|
5297
|
+
const existing = /* @__PURE__ */ new Set();
|
|
5298
|
+
replaceOutsideCode(page.body, (text) => {
|
|
5299
|
+
for (const match of text.matchAll(/\bid=(?:"([^"]+)"|'([^']+)')/g)) existing.add(match[1] ?? match[2]);
|
|
5300
|
+
return text;
|
|
5301
|
+
});
|
|
5302
|
+
const candidateIndexes = /* @__PURE__ */ new Map();
|
|
5303
|
+
for (const heading of headings) {
|
|
5304
|
+
const key = comparable(heading.text);
|
|
5305
|
+
candidateIndexes.set(key, [...candidateIndexes.get(key) ?? [], { index: heading.index, kind: "heading" }]);
|
|
5306
|
+
}
|
|
5307
|
+
for (const cell of tableCells) {
|
|
5308
|
+
const key = comparable(cell.text);
|
|
5309
|
+
candidateIndexes.set(key, [...candidateIndexes.get(key) ?? [], { index: cell.index, kind: "table" }]);
|
|
5310
|
+
}
|
|
5311
|
+
for (const heading of headings) existing.add(heading.text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, ""));
|
|
5312
|
+
const headingAliases = /* @__PURE__ */ new Map();
|
|
5313
|
+
const tableAliases = /* @__PURE__ */ new Map();
|
|
5314
|
+
for (const fragment of fragments) {
|
|
5315
|
+
if (existing.has(fragment)) continue;
|
|
5316
|
+
const matches = candidateIndexes.get(comparable(fragment)) ?? [];
|
|
5317
|
+
let match = matches.length === 1 ? matches[0] : void 0;
|
|
5318
|
+
if (!match && matches.length > 1 && !crossPageRequests.has(`${page.id}#${fragment}`)) {
|
|
5319
|
+
const escaped = fragment.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5320
|
+
const linkPattern = new RegExp(`\\]\\(#${escaped}\\)|\\b(?:href|to)=["']#${escaped}["']`);
|
|
5321
|
+
const linkIndexes = lines.flatMap((line, index2) => linkPattern.test(line) ? [index2] : []);
|
|
5322
|
+
const selections = linkIndexes.map((index2) => matches.filter((candidate) => candidate.index < index2).at(-1));
|
|
5323
|
+
if (selections.length && selections.every((candidate) => candidate?.index === selections[0]?.index)) match = selections[0];
|
|
5324
|
+
}
|
|
5325
|
+
if (!match) continue;
|
|
5326
|
+
const { index, kind } = match;
|
|
5327
|
+
const aliases = kind === "heading" ? headingAliases : tableAliases;
|
|
5328
|
+
aliases.set(index, [...aliases.get(index) ?? [], fragment]);
|
|
5329
|
+
existing.add(fragment);
|
|
5330
|
+
}
|
|
5331
|
+
if (headingAliases.size || tableAliases.size) page.body = lines.flatMap((line, index) => [
|
|
5332
|
+
...(headingAliases.get(index) ?? []).map((fragment) => `<a id="${fragment}"></a>`),
|
|
5333
|
+
tableAliases.has(index) ? line.replace(/^( {0,3}\|\s*)/, (prefix) => `${prefix}${tableAliases.get(index).map((fragment) => `<a id="${fragment}"></a>`).join("")}`) : line
|
|
5334
|
+
]).join("\n");
|
|
5335
|
+
}
|
|
5336
|
+
}
|
|
5337
|
+
function closeOpenCodeFence(body) {
|
|
5338
|
+
let open = null;
|
|
5339
|
+
for (const line of body.split(/\r?\n/)) {
|
|
5340
|
+
const match = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
5341
|
+
if (!match) continue;
|
|
5342
|
+
const marker = match[1][0];
|
|
5343
|
+
if (!open) {
|
|
5344
|
+
open = { marker, length: match[1].length };
|
|
5345
|
+
} else if (marker === open.marker && match[1].length >= open.length && !match[2].trim()) {
|
|
5346
|
+
open = null;
|
|
5347
|
+
}
|
|
5348
|
+
}
|
|
5349
|
+
return open ? `${body}
|
|
5350
|
+
|
|
5351
|
+
${open.marker.repeat(open.length)}` : body;
|
|
5352
|
+
}
|
|
5353
|
+
function addDatedHeadingAliases(body, date) {
|
|
5354
|
+
let open = null;
|
|
5355
|
+
const existingIds = new Set([...body.matchAll(/<a\s+id=["']([^"']+)["']/g)].map((match) => match[1]));
|
|
5356
|
+
return body.split(/\r?\n/).flatMap((line) => {
|
|
5357
|
+
const fence = line.match(/^ {0,3}(`{3,}|~{3,})(.*)$/);
|
|
5358
|
+
if (fence) {
|
|
5359
|
+
const marker = fence[1][0];
|
|
5360
|
+
if (!open) open = { marker, length: fence[1].length };
|
|
5361
|
+
else if (marker === open.marker && fence[1].length >= open.length && !fence[2].trim()) open = null;
|
|
5362
|
+
return [line];
|
|
5363
|
+
}
|
|
5364
|
+
if (open) return [line];
|
|
5365
|
+
const heading = line.match(/^#{2,6}\s+(.+?)\s*#*\s*$/);
|
|
5366
|
+
if (!heading) return [line];
|
|
5367
|
+
const slug = heading[1].replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[`*_]/g, "").normalize("NFKD").toLowerCase().replace(/[^\p{L}\p{N}\s-]/gu, "").trim().replace(/\s+/g, "-");
|
|
5368
|
+
const id = `${date}-${slug}`;
|
|
5369
|
+
if (!slug || existingIds.has(id)) return [line];
|
|
5370
|
+
existingIds.add(id);
|
|
5371
|
+
return [`<a id="${id}" />`, "", line];
|
|
4322
5372
|
}).join("\n");
|
|
4323
5373
|
}
|
|
4324
5374
|
function resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot) {
|
|
5375
|
+
sourcePath = sourcePath.replace(/\\(?=_)/g, "");
|
|
4325
5376
|
const repositoryRelative = sourcePath.startsWith("@site/") ? relative5(repositoryRoot, resolveWithin(siteRoot, sourcePath.slice("@site/".length))).replace(/\\/g, "/") : sourcePath.startsWith("/") ? relative5(repositoryRoot, resolveWithin(siteRoot, sourcePath.replace(/^\/+/, ""))).replace(/\\/g, "/") : relative5(repositoryRoot, resolvePath(dirname4(currentFile), sourcePath)).replace(/\\/g, "/");
|
|
4326
5377
|
const candidate = resolveWithin(repositoryRoot, repositoryRelative);
|
|
4327
5378
|
resolveWithin(repositoryRoot, relative5(repositoryRoot, candidate));
|
|
@@ -4332,12 +5383,12 @@ function globalSnippetAliases(files, repositoryRoot, siteRoot) {
|
|
|
4332
5383
|
for (const file of files) {
|
|
4333
5384
|
const segments = file.relativePath.split("/");
|
|
4334
5385
|
if (!segments.some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase()))) continue;
|
|
4335
|
-
const basenameWithoutExtension =
|
|
5386
|
+
const basenameWithoutExtension = basename3(file.relativePath).replace(/\.mdx?$/i, "");
|
|
4336
5387
|
const conventionalAlias = basenameWithoutExtension.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
4337
5388
|
if (conventionalAlias) aliases.set(conventionalAlias, file.absolutePath);
|
|
4338
5389
|
}
|
|
4339
5390
|
for (const file of files) {
|
|
4340
|
-
if (![".md", ".mdx"].includes(
|
|
5391
|
+
if (![".md", ".mdx"].includes(extname5(file.relativePath).toLowerCase())) continue;
|
|
4341
5392
|
const raw = readFileSync5(file.absolutePath, "utf8");
|
|
4342
5393
|
for (const match of raw.matchAll(SNIPPET_IMPORT_PATTERN)) {
|
|
4343
5394
|
try {
|
|
@@ -4365,9 +5416,13 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced) {
|
|
|
4365
5416
|
} catch {
|
|
4366
5417
|
return null;
|
|
4367
5418
|
}
|
|
4368
|
-
if (!ASSET_EXTENSIONS.has(
|
|
5419
|
+
if (!ASSET_EXTENSIONS.has(extname5(decodedPath).toLowerCase())) return null;
|
|
4369
5420
|
try {
|
|
4370
|
-
const candidate = decodedPath.startsWith("/") ? resolveWithin(siteRoot, decodedPath.replace(/^\/+/, "")) :
|
|
5421
|
+
const candidate = decodedPath.startsWith("/") ? resolveWithin(siteRoot, decodedPath.replace(/^\/+/, "")) : resolveWithinRoot(dirname4(currentFile), decodedPath, siteRoot);
|
|
5422
|
+
const realRoot = realpathSync2(siteRoot);
|
|
5423
|
+
const realCandidate = realpathSync2(candidate);
|
|
5424
|
+
const realRelative = relative5(realRoot, realCandidate);
|
|
5425
|
+
if (realRelative === ".." || realRelative.startsWith(`..${sep2}`) || isAbsolute2(realRelative)) return null;
|
|
4371
5426
|
const siteRelative = relative5(siteRoot, candidate).replace(/\\/g, "/");
|
|
4372
5427
|
resolveWithin(siteRoot, siteRelative);
|
|
4373
5428
|
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile()) return null;
|
|
@@ -4381,15 +5436,7 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced) {
|
|
|
4381
5436
|
}
|
|
4382
5437
|
}
|
|
4383
5438
|
function rewriteFernIdRenameLinks(body, renames) {
|
|
4384
|
-
|
|
4385
|
-
return body.split("\n").map((line) => {
|
|
4386
|
-
const fence = line.match(/^\s*(`{3,}|~{3,})/);
|
|
4387
|
-
if (fence) {
|
|
4388
|
-
if (!codeFence) codeFence = fence[1][0];
|
|
4389
|
-
else if (fence[1][0] === codeFence) codeFence = null;
|
|
4390
|
-
return line;
|
|
4391
|
-
}
|
|
4392
|
-
if (codeFence) return line;
|
|
5439
|
+
return replaceOutsideCode(body, (text) => {
|
|
4393
5440
|
const rewriteTarget = (target) => {
|
|
4394
5441
|
const suffixIndex = target.search(/[?#]/);
|
|
4395
5442
|
const path = (suffixIndex >= 0 ? target.slice(0, suffixIndex) : target).replace(/^\/+/, "");
|
|
@@ -4397,8 +5444,8 @@ function rewriteFernIdRenameLinks(body, renames) {
|
|
|
4397
5444
|
const renamed = renames.get(path);
|
|
4398
5445
|
return renamed ? `/${renamed}${suffix}` : target;
|
|
4399
5446
|
};
|
|
4400
|
-
return
|
|
4401
|
-
})
|
|
5447
|
+
return text.replace(/(\]\()\/([^\s)]+)(?=[\s)]|$)/g, (_match, prefix, target) => `${prefix}${rewriteTarget(`/${target}`)}`).replace(/(\bhref=")\/([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${rewriteTarget(`/${target}`)}${suffix}`);
|
|
5448
|
+
});
|
|
4402
5449
|
}
|
|
4403
5450
|
function rewriteRepositoryAssetLinks(body, currentFile, siteRoot, onReferenced) {
|
|
4404
5451
|
return body.replace(/(!?\[[^\]]*\]\()(<[^>]+>|[^)\s]+)([^)]*\))/g, (original, opening, destination, closing) => {
|
|
@@ -4428,8 +5475,10 @@ function locallyDeclaredNames(raw) {
|
|
|
4428
5475
|
}
|
|
4429
5476
|
function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0, siteRoot = repositoryRoot, globalAliases = /* @__PURE__ */ new Map()) {
|
|
4430
5477
|
if (depth >= 8) return raw;
|
|
5478
|
+
raw = normalizeIndentedFences(raw);
|
|
4431
5479
|
const snippets = /* @__PURE__ */ new Map();
|
|
4432
|
-
const
|
|
5480
|
+
const preservedDeclarations = /* @__PURE__ */ new Map();
|
|
5481
|
+
let withoutImports = replaceOutsideCode(raw, (source) => source.replace(
|
|
4433
5482
|
SNIPPET_IMPORT_PATTERN,
|
|
4434
5483
|
(_statement, namedComponent, defaultComponent, sourcePath) => {
|
|
4435
5484
|
const componentName = namedComponent ?? defaultComponent;
|
|
@@ -4445,7 +5494,9 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
4445
5494
|
siteRoot,
|
|
4446
5495
|
globalAliases
|
|
4447
5496
|
);
|
|
4448
|
-
|
|
5497
|
+
const declaration = statefulSnippetDeclaration(nested, componentName);
|
|
5498
|
+
if (declaration) preservedDeclarations.set(componentName, declaration);
|
|
5499
|
+
else snippets.set(componentName, snippetComponentBody(nested, componentName));
|
|
4449
5500
|
return "";
|
|
4450
5501
|
} catch {
|
|
4451
5502
|
warnings.push({
|
|
@@ -4457,7 +5508,40 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
4457
5508
|
return "";
|
|
4458
5509
|
}
|
|
4459
5510
|
}
|
|
4460
|
-
);
|
|
5511
|
+
));
|
|
5512
|
+
withoutImports = replaceOutsideCode(withoutImports, (source) => source.replace(
|
|
5513
|
+
SNIPPET_VALUE_IMPORT_PATTERN,
|
|
5514
|
+
(statement, names, sourcePath) => {
|
|
5515
|
+
const bindings = names.split(",").map((name) => name.trim()).filter(Boolean).map((name) => {
|
|
5516
|
+
const parsed = name.match(/^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
|
|
5517
|
+
return parsed ? { exported: parsed[1], local: parsed[2] ?? parsed[1] } : null;
|
|
5518
|
+
});
|
|
5519
|
+
if (bindings.length === 0 || bindings.some((binding) => !binding)) return statement;
|
|
5520
|
+
try {
|
|
5521
|
+
const candidate = resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot);
|
|
5522
|
+
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile()) return statement;
|
|
5523
|
+
const snippetSource = readFileSync5(candidate, "utf8");
|
|
5524
|
+
const values = staticNamedSnippetValues(snippetSource);
|
|
5525
|
+
const declarations = [];
|
|
5526
|
+
const components = [];
|
|
5527
|
+
for (const binding of bindings) {
|
|
5528
|
+
const value = values.get(binding.exported);
|
|
5529
|
+
if (value !== void 0) {
|
|
5530
|
+
declarations.push(`export const ${binding.local} = ${value};`);
|
|
5531
|
+
continue;
|
|
5532
|
+
}
|
|
5533
|
+
if (!/^[A-Z]/.test(binding.exported)) return statement;
|
|
5534
|
+
const body = snippetComponentBody(snippetSource, binding.exported);
|
|
5535
|
+
if (body === snippetSource) return statement;
|
|
5536
|
+
components.push([binding.local, body]);
|
|
5537
|
+
}
|
|
5538
|
+
for (const [name, body] of components) snippets.set(name, body);
|
|
5539
|
+
return declarations.join("\n");
|
|
5540
|
+
} catch {
|
|
5541
|
+
return statement;
|
|
5542
|
+
}
|
|
5543
|
+
}
|
|
5544
|
+
));
|
|
4461
5545
|
const localNames = locallyDeclaredNames(withoutImports);
|
|
4462
5546
|
for (const [componentName, candidate] of globalAliases) {
|
|
4463
5547
|
if (snippets.has(componentName) || localNames.has(componentName) || !new RegExp(`<${componentName}(?:\\s|/?>)`).test(withoutImports)) continue;
|
|
@@ -4470,14 +5554,16 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
4470
5554
|
siteRoot,
|
|
4471
5555
|
globalAliases
|
|
4472
5556
|
);
|
|
4473
|
-
|
|
5557
|
+
const declaration = statefulSnippetDeclaration(nested, componentName);
|
|
5558
|
+
if (declaration) preservedDeclarations.set(componentName, declaration);
|
|
5559
|
+
else snippets.set(componentName, snippetComponentBody(nested, componentName));
|
|
4474
5560
|
}
|
|
4475
5561
|
let result = withoutImports;
|
|
4476
5562
|
for (const [componentName, snippet] of snippets) {
|
|
4477
5563
|
result = result.replace(new RegExp(`<${componentName}((?:\\s[^>]*)?)\\s*/>`, "g"), (_tag, attributes) => {
|
|
4478
5564
|
return interpolateSnippet(snippet, attributes);
|
|
4479
|
-
}).replace(new RegExp(`<${componentName}((?:\\s[^>]*)?)>(
|
|
4480
|
-
return interpolateSnippet(snippet, attributes);
|
|
5565
|
+
}).replace(new RegExp(`<${componentName}((?:\\s[^>]*)?)>([\\s\\S]*?)<\\/${componentName}>`, "g"), (_tag, attributes, children) => {
|
|
5566
|
+
return interpolateSnippet(snippet, attributes, children);
|
|
4481
5567
|
});
|
|
4482
5568
|
}
|
|
4483
5569
|
result = result.replace(SNIPPET_TAG_PATTERN, (_tag, doubleQuoted, singleQuoted) => {
|
|
@@ -4503,13 +5589,25 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
4503
5589
|
return `{/* Missing snippet: ${filePath} */}`;
|
|
4504
5590
|
}
|
|
4505
5591
|
});
|
|
4506
|
-
|
|
5592
|
+
if (preservedDeclarations.size > 0) {
|
|
5593
|
+
const frontmatter = result.match(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/);
|
|
5594
|
+
const prefix = frontmatter?.[0] ?? "";
|
|
5595
|
+
result = `${prefix}${[...preservedDeclarations.values()].join("\n\n")}
|
|
5596
|
+
|
|
5597
|
+
${result.slice(prefix.length)}`;
|
|
5598
|
+
}
|
|
5599
|
+
return depth === 0 ? hoistMdxImports(result) : result;
|
|
4507
5600
|
}
|
|
4508
5601
|
function injectOpenApiSpecs(config, specs) {
|
|
4509
5602
|
let tabs = config.tabs.map((tab) => ({ ...tab }));
|
|
4510
5603
|
for (const spec of specs) {
|
|
4511
5604
|
const apiTab = spec.tabLabel ? tabs.find((tab) => tab.tab === spec.tabLabel) : tabs.find((tab) => tab.tab.toLowerCase().includes("api"));
|
|
4512
|
-
if (apiTab) apiTab.api = {
|
|
5605
|
+
if (apiTab) apiTab.api = {
|
|
5606
|
+
source: `/${spec.filename}`,
|
|
5607
|
+
// An API-only tab needs generated endpoint navigation; an authored
|
|
5608
|
+
// page tab keeps its own groups alongside the bound spec.
|
|
5609
|
+
...apiTab.groups?.length || apiTab.pages?.length ? { navigation: false } : {}
|
|
5610
|
+
};
|
|
4513
5611
|
else tabs = [...tabs, { tab: spec.tabLabel ?? "API Reference", api: { source: `/${spec.filename}` } }];
|
|
4514
5612
|
}
|
|
4515
5613
|
return { ...config, tabs };
|
|
@@ -4547,30 +5645,6 @@ function migrateRepository(options) {
|
|
|
4547
5645
|
}
|
|
4548
5646
|
return platform === "mintlify" ? "" : detectRepositoryDocsDir(repositoryDir);
|
|
4549
5647
|
})();
|
|
4550
|
-
if (platform === "docusaurus" && docusaurusProjectRoot) {
|
|
4551
|
-
for (const [directory, label] of [
|
|
4552
|
-
["versioned_docs", "versioned docs"],
|
|
4553
|
-
["versioned_sidebars", "versioned sidebars"]
|
|
4554
|
-
]) {
|
|
4555
|
-
const path = resolveWithin(docusaurusProjectRoot, directory);
|
|
4556
|
-
if (!existsSync5(path) || !lstatSync5(path).isDirectory()) continue;
|
|
4557
|
-
const versions = readdirSync(path, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
4558
|
-
if (versions.length) warnings.push({
|
|
4559
|
-
code: "unsupported-config",
|
|
4560
|
-
message: `Docusaurus ${label} were skipped; only current docs were imported. Skipped: ${versions.join(", ")}.`,
|
|
4561
|
-
source: directory
|
|
4562
|
-
});
|
|
4563
|
-
}
|
|
4564
|
-
const i18nPath = resolveWithin(docusaurusProjectRoot, "i18n");
|
|
4565
|
-
if (existsSync5(i18nPath) && lstatSync5(i18nPath).isDirectory()) {
|
|
4566
|
-
const locales = readdirSync(i18nPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
4567
|
-
if (locales.length) warnings.push({
|
|
4568
|
-
code: "unsupported-config",
|
|
4569
|
-
message: `Docusaurus localized content was skipped; only the default locale was imported. Skipped locales: ${locales.join(", ")}.`,
|
|
4570
|
-
source: "i18n"
|
|
4571
|
-
});
|
|
4572
|
-
}
|
|
4573
|
-
}
|
|
4574
5648
|
const componentRoot = mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir;
|
|
4575
5649
|
const componentMigrator = platform === "mintlify" || platform === "docusaurus" || platform === "fern" ? createComponentMigrator(componentRoot, repositoryDir, warnings, componentSourceIdentity(options.sourceUrl, repositoryDir, componentRoot)) : void 0;
|
|
4576
5650
|
let docsConfig = { tabs: [] };
|
|
@@ -4580,7 +5654,14 @@ function migrateRepository(options) {
|
|
|
4580
5654
|
let docusaurusSidebars = null;
|
|
4581
5655
|
let mintlifyConfig = null;
|
|
4582
5656
|
let fernRawConfig = null;
|
|
5657
|
+
let fernBasePath = "";
|
|
5658
|
+
let fernVersionPath = "";
|
|
4583
5659
|
let fernApiSections = [];
|
|
5660
|
+
let fernChangelogIndexes = [];
|
|
5661
|
+
const fernReferencedPaths = /* @__PURE__ */ new Set();
|
|
5662
|
+
const fernSourceLinkAliases = /* @__PURE__ */ new Map();
|
|
5663
|
+
const fernNavTitles = /* @__PURE__ */ new Map();
|
|
5664
|
+
const fernHiddenIds = /* @__PURE__ */ new Set();
|
|
4584
5665
|
if (platform === "mintlify") {
|
|
4585
5666
|
try {
|
|
4586
5667
|
const config = readMintlifyConfig(mintlifyProjectRoot ?? repositoryDir);
|
|
@@ -4609,11 +5690,28 @@ function migrateRepository(options) {
|
|
|
4609
5690
|
const fernConfig = readFernConfig(fernProjectRoot);
|
|
4610
5691
|
if (fernConfig) {
|
|
4611
5692
|
fernRawConfig = fernConfig.config;
|
|
4612
|
-
|
|
5693
|
+
fernBasePath = fernSourceBasePath(fernConfig.config);
|
|
5694
|
+
fernVersionPath = fernDefaultVersionPath(fernConfig.config);
|
|
5695
|
+
const projected = projectFernNavigation({ config: fernConfig.config, fernRoot: fernProjectRoot, repositoryRoot: repositoryDir });
|
|
4613
5696
|
docsConfig = projected.docsConfig;
|
|
4614
5697
|
warnings.push(...projected.warnings);
|
|
4615
5698
|
fernApiSections = projected.apiSections;
|
|
5699
|
+
fernChangelogIndexes = projected.changelogIndexes;
|
|
4616
5700
|
for (const [index, descriptor] of projected.descriptors.entries()) {
|
|
5701
|
+
fernReferencedPaths.add(descriptor.sourcePath);
|
|
5702
|
+
if (descriptor.navTitle && !fernNavTitles.has(descriptor.navigationId)) {
|
|
5703
|
+
fernNavTitles.set(descriptor.navigationId, descriptor.navTitle);
|
|
5704
|
+
}
|
|
5705
|
+
if (descriptor.hidden) fernHiddenIds.add(descriptor.navigationId);
|
|
5706
|
+
const sourceTail = descriptor.sourcePath.match(/(?:^|\/)pages\/(.+)\.mdx?$/i)?.[1]?.replace(/\/index$/i, "");
|
|
5707
|
+
if (sourceTail) {
|
|
5708
|
+
const routeSegments = descriptor.navigationId.split("/");
|
|
5709
|
+
const sourceSegments = sourceTail.split("/");
|
|
5710
|
+
if (routeSegments.length === sourceSegments.length + 1) {
|
|
5711
|
+
const alias = `${routeSegments[0]}/${sourceTail}`;
|
|
5712
|
+
if (alias !== descriptor.navigationId && !fernSourceLinkAliases.has(alias)) fernSourceLinkAliases.set(alias, descriptor.navigationId);
|
|
5713
|
+
}
|
|
5714
|
+
}
|
|
4617
5715
|
const key = normalizedReferenceKey(descriptor.sourcePath);
|
|
4618
5716
|
if (!referenceMap.has(key)) referenceMap.set(key, { navigationId: descriptor.navigationId });
|
|
4619
5717
|
const exactKey = exactReferenceKey(descriptor.sourcePath);
|
|
@@ -4630,7 +5728,7 @@ function migrateRepository(options) {
|
|
|
4630
5728
|
}
|
|
4631
5729
|
if (platform === "docusaurus") {
|
|
4632
5730
|
try {
|
|
4633
|
-
docusaurusSidebars = options.docusaurusSkipSidebar || !docusaurusProjectRoot ? null : readDocusaurusSidebars(docusaurusProjectRoot);
|
|
5731
|
+
docusaurusSidebars = options.docusaurusSkipSidebar || !docusaurusProjectRoot ? null : readDocusaurusSidebars(docusaurusProjectRoot, options.docusaurusSidebarPath);
|
|
4634
5732
|
} catch (error) {
|
|
4635
5733
|
warnings.push({
|
|
4636
5734
|
code: "unsupported-config",
|
|
@@ -4644,6 +5742,19 @@ function migrateRepository(options) {
|
|
|
4644
5742
|
}
|
|
4645
5743
|
const mintignoreMatcher = platform === "mintlify" && mintlifyProjectRoot ? readMintignoreMatcher(mintlifyProjectRoot) : null;
|
|
4646
5744
|
const files = mintignoreMatcher ? scanFiles(contentRoot, repositoryDir, warnings).filter((file) => !mintignoreMatcher.ignores(file.relativePath)) : scanFiles(contentRoot, repositoryDir, warnings);
|
|
5745
|
+
if (platform === "fern" && fernProjectRoot) {
|
|
5746
|
+
const scanned = new Set(files.map((file) => file.absolutePath));
|
|
5747
|
+
for (const sourcePath of fernReferencedPaths) {
|
|
5748
|
+
try {
|
|
5749
|
+
const absolutePath = resolveWithinRoot(fernProjectRoot, sourcePath, repositoryDir);
|
|
5750
|
+
if (scanned.has(absolutePath) || !existsSync5(absolutePath) || !lstatSync5(absolutePath).isFile()) continue;
|
|
5751
|
+
files.push({ absolutePath, relativePath: sourcePath });
|
|
5752
|
+
scanned.add(absolutePath);
|
|
5753
|
+
} catch {
|
|
5754
|
+
warnings.push({ code: "missing-page", message: "A Fern navigation path could not be read within this repository.", source: sourcePath });
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5757
|
+
}
|
|
4647
5758
|
const pages = [];
|
|
4648
5759
|
const assets = [];
|
|
4649
5760
|
const referencedAssetPaths = /* @__PURE__ */ new Map();
|
|
@@ -4673,16 +5784,16 @@ function migrateRepository(options) {
|
|
|
4673
5784
|
continue;
|
|
4674
5785
|
}
|
|
4675
5786
|
const sourceSegments = file.relativePath.split("/");
|
|
4676
|
-
if (sourceSegments.some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase())) || platform === "docusaurus" &&
|
|
5787
|
+
if (sourceSegments.some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase())) || platform === "docusaurus" && basename3(file.relativePath).startsWith("_")) {
|
|
4677
5788
|
skipped++;
|
|
4678
5789
|
continue;
|
|
4679
5790
|
}
|
|
4680
|
-
const rootFilename = !file.relativePath.includes("/") ?
|
|
5791
|
+
const rootFilename = !file.relativePath.includes("/") ? basename3(file.relativePath).toLowerCase() : "";
|
|
4681
5792
|
if (REPOSITORY_ONLY_DOCUMENTS.has(rootFilename) && !exactReferenceMap.has(exactReferenceKey(file.relativePath))) {
|
|
4682
5793
|
skipped++;
|
|
4683
5794
|
continue;
|
|
4684
5795
|
}
|
|
4685
|
-
if (![".md", ".mdx"].includes(
|
|
5796
|
+
if (![".md", ".mdx"].includes(extname5(file.relativePath).toLowerCase())) {
|
|
4686
5797
|
skipped++;
|
|
4687
5798
|
warnings.push({ code: "skipped-file", message: "Only Markdown and MDX are imported without an explicit converter.", source: file.relativePath });
|
|
4688
5799
|
continue;
|
|
@@ -4793,6 +5904,11 @@ function migrateRepository(options) {
|
|
|
4793
5904
|
skipped++;
|
|
4794
5905
|
continue;
|
|
4795
5906
|
}
|
|
5907
|
+
if (platform === "fern" && !page.navTitle) {
|
|
5908
|
+
const navTitle = fernNavTitles.get(navigationId);
|
|
5909
|
+
if (navTitle && navTitle !== page.title) page.navTitle = navTitle;
|
|
5910
|
+
}
|
|
5911
|
+
if (platform === "fern" && fernHiddenIds.has(navigationId)) page.hidden = true;
|
|
4796
5912
|
if (platform === "mintlify" && mintlifyProjectRoot) {
|
|
4797
5913
|
page.body = rewriteRepositoryAssetLinks(page.body, file.absolutePath, mintlifyProjectRoot, (assetPath) => {
|
|
4798
5914
|
addAssetReference(assetPath, file.relativePath);
|
|
@@ -4802,6 +5918,8 @@ function migrateRepository(options) {
|
|
|
4802
5918
|
page.body = rewriteRepositoryAssetLinks(page.body, file.absolutePath, fernProjectRoot, (assetPath) => {
|
|
4803
5919
|
addAssetReference(assetPath, file.relativePath);
|
|
4804
5920
|
});
|
|
5921
|
+
page.body = stripFernBasePathFromLinks(page.body, fernBasePath);
|
|
5922
|
+
page.body = stripFernBasePathFromLinks(page.body, fernVersionPath);
|
|
4805
5923
|
}
|
|
4806
5924
|
if (platform === "mintlify" || platform === "docusaurus" || platform === "fern") {
|
|
4807
5925
|
page.body = replaceLinkWithAnchor(page.body);
|
|
@@ -4882,7 +6000,7 @@ function migrateRepository(options) {
|
|
|
4882
6000
|
const assetCandidates = [];
|
|
4883
6001
|
for (const file of [...files, ...repositoryAssets]) {
|
|
4884
6002
|
const firstSegment = file.relativePath.split("/", 1)[0].toLowerCase();
|
|
4885
|
-
if (!ASSET_EXTENSIONS.has(
|
|
6003
|
+
if (!ASSET_EXTENSIONS.has(extname5(file.relativePath).toLowerCase())) continue;
|
|
4886
6004
|
if (platform !== "mintlify" && !ASSET_DIRECTORIES.has(firstSegment)) continue;
|
|
4887
6005
|
const isDocusaurusStatic = platform === "docusaurus" && firstSegment === "static";
|
|
4888
6006
|
const assetPath = normalizeAssetPath(firstSegment === "public" ? file.relativePath.slice("public/".length) : isDocusaurusStatic ? file.relativePath.slice("static/".length) : file.relativePath);
|
|
@@ -4938,10 +6056,19 @@ function migrateRepository(options) {
|
|
|
4938
6056
|
const descriptorByNavigationId = new Map(
|
|
4939
6057
|
docusaurusDescriptors.map((descriptor) => [descriptor.navigationId, descriptor])
|
|
4940
6058
|
);
|
|
6059
|
+
const sourceOrigin = docusaurusProjectRoot ? readDocusaurusSiteOrigin(docusaurusProjectRoot) : void 0;
|
|
6060
|
+
const externalizedLinks = /* @__PURE__ */ new Set();
|
|
4941
6061
|
for (const page of pages) {
|
|
4942
6062
|
const descriptor = descriptorByNavigationId.get(page.navigationId);
|
|
4943
|
-
if (descriptor) page.body = rewriteDocusaurusLinks(page.body, descriptor, docusaurusDescriptors
|
|
6063
|
+
if (descriptor) page.body = rewriteDocusaurusLinks(page.body, descriptor, docusaurusDescriptors, {
|
|
6064
|
+
sourceOrigin,
|
|
6065
|
+
onExternalLink: (target) => externalizedLinks.add(target)
|
|
6066
|
+
});
|
|
4944
6067
|
}
|
|
6068
|
+
if (externalizedLinks.size > 0) warnings.push({
|
|
6069
|
+
code: "unsupported-config",
|
|
6070
|
+
message: `${externalizedLinks.size} link(s) outside the imported Docusaurus docs still point to ${sourceOrigin}; migrate those sections separately if this site must be fully independent.`
|
|
6071
|
+
});
|
|
4945
6072
|
if (docusaurusProjectRoot && !options.docusaurusSkipRedirects) {
|
|
4946
6073
|
const redirects = readDocusaurusRedirects(docusaurusProjectRoot, warnings);
|
|
4947
6074
|
if (redirects.length > 0) docsConfig = { ...docsConfig, redirects: [...docsConfig.redirects ?? [], ...redirects] };
|
|
@@ -4972,6 +6099,11 @@ function migrateRepository(options) {
|
|
|
4972
6099
|
} : {}
|
|
4973
6100
|
};
|
|
4974
6101
|
}
|
|
6102
|
+
if (platform === "fern" && fernSourceLinkAliases.size > 0) {
|
|
6103
|
+
const imported = new Set(pages.map((page) => page.id));
|
|
6104
|
+
const aliases = new Map([...fernSourceLinkAliases].map(([source, target]) => [source, fernIdRenames.get(target) ?? target]).filter(([, target]) => imported.has(target)));
|
|
6105
|
+
for (const page of pages) page.body = rewriteFernIdRenameLinks(page.body, aliases);
|
|
6106
|
+
}
|
|
4975
6107
|
if (docsConfig.tabs.length === 0) docsConfig = buildNavigationFromPages(pages);
|
|
4976
6108
|
if (platform === "fern" && fernProjectRoot) {
|
|
4977
6109
|
const sections = fernApiSections.length > 0 ? fernApiSections : [{ nameExplicit: false }];
|
|
@@ -4981,7 +6113,7 @@ function migrateRepository(options) {
|
|
|
4981
6113
|
const resolution = findFernConfiguredOpenApi(fernProjectRoot, repositoryDir, section.name, section.nameExplicit, warnings);
|
|
4982
6114
|
const spec = resolution.spec ?? (!section.nameExplicit ? findOpenApi(files) : null);
|
|
4983
6115
|
if (spec) {
|
|
4984
|
-
let filename =
|
|
6116
|
+
let filename = basename3(spec.relativePath);
|
|
4985
6117
|
const existingSource = specFilenameSources.get(filename);
|
|
4986
6118
|
if (existingSource && existingSource !== spec.absolutePath) {
|
|
4987
6119
|
const prefix = section.name ? `${section.name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "")}-` : `${specFilenameSources.size + 1}-`;
|
|
@@ -5032,7 +6164,7 @@ function migrateRepository(options) {
|
|
|
5032
6164
|
} else {
|
|
5033
6165
|
const fallback = findOpenApi(files);
|
|
5034
6166
|
if (fallback) {
|
|
5035
|
-
const filename =
|
|
6167
|
+
const filename = basename3(fallback.relativePath);
|
|
5036
6168
|
if (!assets.some((asset) => asset.path === filename)) {
|
|
5037
6169
|
assets.push({ path: filename, content: readFileSync5(fallback.absolutePath) });
|
|
5038
6170
|
}
|
|
@@ -5045,9 +6177,25 @@ function migrateRepository(options) {
|
|
|
5045
6177
|
const aliases = routeAliases.filter((redirect) => !sources.has(redirect.source));
|
|
5046
6178
|
if (aliases.length > 0) docsConfig = { ...docsConfig, redirects: [...docsConfig.redirects ?? [], ...aliases] };
|
|
5047
6179
|
docsConfig = addMintlifyHomepageRedirects(addMintlifyDirectoryRedirects(docsConfig, pages), pages);
|
|
6180
|
+
const reachablePaths = /* @__PURE__ */ new Set([
|
|
6181
|
+
...pages.map((page) => page.id),
|
|
6182
|
+
...(docsConfig.redirects ?? []).map((redirect) => redirect.source.replace(/^\//, ""))
|
|
6183
|
+
]);
|
|
6184
|
+
for (const page of pages) page.body = rewriteMintlifyMountedLinks(page.body, reachablePaths);
|
|
5048
6185
|
}
|
|
5049
6186
|
if (platform === "docusaurus" && docusaurusProjectRoot && !options.docusaurusSkipPlugins) {
|
|
5050
|
-
|
|
6187
|
+
const additionalRoots = [
|
|
6188
|
+
...additionalDocusaurusPluginRoots(repositoryDir, docusaurusProjectRoot, warnings).map((plugin) => ({
|
|
6189
|
+
...plugin,
|
|
6190
|
+
label: plugin.routePrefix.split(/[-_/]/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ")
|
|
6191
|
+
})),
|
|
6192
|
+
...docusaurusArchiveRoots(docusaurusProjectRoot, repositoryDir, warnings)
|
|
6193
|
+
];
|
|
6194
|
+
for (const plugin of additionalRoots) {
|
|
6195
|
+
if (discovered >= MAX_SOURCE_FILES) {
|
|
6196
|
+
warnings.push({ code: "limit-reached", message: `Docusaurus docs import stopped at the ${MAX_SOURCE_FILES}-file discovery budget.` });
|
|
6197
|
+
break;
|
|
6198
|
+
}
|
|
5051
6199
|
if (plugin.docsDir === configuredDocsDir) continue;
|
|
5052
6200
|
const pluginBundle = migrateRepository({
|
|
5053
6201
|
...options,
|
|
@@ -5055,22 +6203,27 @@ function migrateRepository(options) {
|
|
|
5055
6203
|
platform: "docusaurus",
|
|
5056
6204
|
docusaurusRoutePrefix: plugin.routePrefix,
|
|
5057
6205
|
docusaurusSkipPlugins: true,
|
|
5058
|
-
docusaurusSkipSidebar:
|
|
6206
|
+
docusaurusSkipSidebar: !plugin.sidebarPath,
|
|
6207
|
+
docusaurusSidebarPath: plugin.sidebarPath,
|
|
5059
6208
|
docusaurusSkipAssets: true,
|
|
5060
6209
|
docusaurusSkipRedirects: true
|
|
5061
6210
|
});
|
|
5062
6211
|
for (const page of pluginBundle.pages) {
|
|
5063
|
-
if (seenPageIds.has(page.id))
|
|
6212
|
+
if (seenPageIds.has(page.id)) {
|
|
6213
|
+
skipped++;
|
|
6214
|
+
warnings.push({ code: "collision", message: `Docusaurus docs roots both map to ${page.id}; the first page was kept.`, source: page.source });
|
|
6215
|
+
continue;
|
|
6216
|
+
}
|
|
5064
6217
|
seenPageIds.add(page.id);
|
|
5065
6218
|
pages.push(page);
|
|
5066
6219
|
}
|
|
5067
|
-
const
|
|
6220
|
+
const assetPaths2 = new Set(assets.map((asset) => asset.path));
|
|
5068
6221
|
for (const asset of pluginBundle.assets) {
|
|
5069
|
-
if (
|
|
5070
|
-
|
|
6222
|
+
if (assetPaths2.has(asset.path)) continue;
|
|
6223
|
+
assetPaths2.add(asset.path);
|
|
5071
6224
|
assets.push(asset);
|
|
5072
6225
|
}
|
|
5073
|
-
const pluginLabel = plugin.
|
|
6226
|
+
const pluginLabel = plugin.label;
|
|
5074
6227
|
docsConfig.tabs.push(...pluginBundle.docsConfig.tabs.map((tab, index) => ({
|
|
5075
6228
|
...tab,
|
|
5076
6229
|
tab: index === 0 ? pluginLabel : `${pluginLabel}: ${tab.tab}`
|
|
@@ -5080,21 +6233,234 @@ function migrateRepository(options) {
|
|
|
5080
6233
|
skipped += pluginBundle.stats.skipped;
|
|
5081
6234
|
}
|
|
5082
6235
|
}
|
|
6236
|
+
if (platform === "docusaurus") {
|
|
6237
|
+
const importedRoutes = new Set(pages.map((page) => page.navigationId));
|
|
6238
|
+
docsConfig.tabs = docsConfig.tabs.map((tab) => {
|
|
6239
|
+
if (tab.href) return tab;
|
|
6240
|
+
const root = tab.tab.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-");
|
|
6241
|
+
if (!root || !importedRoutes.has(root)) return tab;
|
|
6242
|
+
return {
|
|
6243
|
+
...tab,
|
|
6244
|
+
href: `/${root}`,
|
|
6245
|
+
...tab.pages ? { pages: tab.pages.map((node) => {
|
|
6246
|
+
if (typeof node === "string") return node;
|
|
6247
|
+
const index = node.pages.indexOf(root);
|
|
6248
|
+
return index > 0 ? { ...node, pages: [root, ...node.pages.filter((page) => page !== root)] } : node;
|
|
6249
|
+
}) } : {},
|
|
6250
|
+
...tab.groups ? { groups: tab.groups.map((group) => {
|
|
6251
|
+
const index = group.pages.indexOf(root);
|
|
6252
|
+
return index > 0 ? { ...group, pages: [root, ...group.pages.filter((page) => page !== root)] } : group;
|
|
6253
|
+
}) } : {}
|
|
6254
|
+
};
|
|
6255
|
+
});
|
|
6256
|
+
}
|
|
6257
|
+
if (platform === "fern") {
|
|
6258
|
+
const byId = new Map(pages.map((page) => [page.navigationId, page]));
|
|
6259
|
+
for (const changelog of fernChangelogIndexes) {
|
|
6260
|
+
let index = byId.get(changelog.route);
|
|
6261
|
+
const entries = changelog.entries.map((id) => byId.get(fernIdRenames.get(id) ?? id)).filter((page) => Boolean(page)).slice(0, 10);
|
|
6262
|
+
if (entries.length === 0) continue;
|
|
6263
|
+
if (!index) {
|
|
6264
|
+
index = {
|
|
6265
|
+
id: changelog.route,
|
|
6266
|
+
navigationId: changelog.route,
|
|
6267
|
+
title: "Release Notes",
|
|
6268
|
+
description: "Latest release notes and product updates.",
|
|
6269
|
+
descriptionPlacement: "body",
|
|
6270
|
+
keywords: [],
|
|
6271
|
+
body: "",
|
|
6272
|
+
source: entries[0].source
|
|
6273
|
+
};
|
|
6274
|
+
pages.push(index);
|
|
6275
|
+
byId.set(index.navigationId, index);
|
|
6276
|
+
docsConfig.redirects = docsConfig.redirects?.filter((redirect) => redirect.source !== `/${changelog.route}`);
|
|
6277
|
+
}
|
|
6278
|
+
if (index.body.trim()) continue;
|
|
6279
|
+
index.description ||= "Latest release notes and product updates.";
|
|
6280
|
+
index.body = entries.map((entry) => {
|
|
6281
|
+
const date = entry.source.match(/\d{4}-\d{2}-\d{2}/)?.[0];
|
|
6282
|
+
const escapeText = (value) => value.replace(/([\\{}<>\[\]*_`])/g, "\\$1");
|
|
6283
|
+
const body = /^(?:import|export)\s.+(?:from\s|\{)/m.test(entry.body) ? escapeText(entry.description) : closeOpenCodeFence(date ? addDatedHeadingAliases(entry.body.trim(), date) : entry.body.trim()) || escapeText(entry.description);
|
|
6284
|
+
const dateAnchor = date ? `<a id="${date}T00:00:00.000Z" />
|
|
6285
|
+
|
|
6286
|
+
` : "";
|
|
6287
|
+
return `${dateAnchor}## ${escapeText(entry.title)}
|
|
6288
|
+
|
|
6289
|
+
${date ? `**${date}** \xB7 ` : ""}[Read release note](/${entry.id})
|
|
6290
|
+
|
|
6291
|
+
${body}`;
|
|
6292
|
+
}).join("\n\n---\n\n");
|
|
6293
|
+
}
|
|
6294
|
+
}
|
|
5083
6295
|
docsConfig = pruneMissingNavigationPages(docsConfig, new Set(pages.map((page) => page.navigationId)));
|
|
6296
|
+
preserveLinkedAnchors(pages);
|
|
6297
|
+
if (platform === "fern" && fernRawConfig) {
|
|
6298
|
+
const sourceSiteUrl = fernSourceSiteUrl(fernRawConfig);
|
|
6299
|
+
if (sourceSiteUrl) {
|
|
6300
|
+
const externalized = /* @__PURE__ */ new Set();
|
|
6301
|
+
const pageIds = new Set(pages.map((page) => page.id));
|
|
6302
|
+
for (const page of pages) page.body = externalizeMissingFernLinks(page.body, pageIds, sourceSiteUrl, externalized);
|
|
6303
|
+
if (externalized.size > 0) warnings.push({
|
|
6304
|
+
code: "unsupported-config",
|
|
6305
|
+
message: `${externalized.size} unresolved Fern link(s) still point to ${sourceSiteUrl}; import those sections separately for a fully independent site.`
|
|
6306
|
+
});
|
|
6307
|
+
}
|
|
6308
|
+
}
|
|
6309
|
+
if (docsConfig.redirects) {
|
|
6310
|
+
docsConfig = {
|
|
6311
|
+
...docsConfig,
|
|
6312
|
+
redirects: docsConfig.redirects.filter((redirect) => {
|
|
6313
|
+
const literalRoute = (route) => route.startsWith("/") && !/[?:*#()[\]{}]/.test(route) ? route.replace(/\/+$/, "") || "/" : null;
|
|
6314
|
+
const sourceRoute = literalRoute(redirect.source);
|
|
6315
|
+
if (sourceRoute && sourceRoute === literalRoute(redirect.destination)) {
|
|
6316
|
+
warnings.push({
|
|
6317
|
+
code: "unsupported-config",
|
|
6318
|
+
message: `Self-redirect from ${redirect.source} to ${redirect.destination} was omitted.`
|
|
6319
|
+
});
|
|
6320
|
+
return false;
|
|
6321
|
+
}
|
|
6322
|
+
if (!/[+?()[\]{}]/.test(redirect.source)) return true;
|
|
6323
|
+
warnings.push({
|
|
6324
|
+
code: "unsupported-config",
|
|
6325
|
+
message: `Redirect from ${redirect.source} contains route-pattern syntax Next.js cannot express literally and was omitted.`
|
|
6326
|
+
});
|
|
6327
|
+
return false;
|
|
6328
|
+
})
|
|
6329
|
+
};
|
|
6330
|
+
}
|
|
5084
6331
|
if (pages.length === 0) {
|
|
5085
6332
|
const reasons = warnings.map((warning) => warning.message);
|
|
5086
6333
|
throw new Error(reasons.length ? `No importable Markdown or MDX pages were found in the repository. Warnings encountered during migration:
|
|
5087
6334
|
${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown or MDX pages were found in the repository.");
|
|
5088
6335
|
}
|
|
5089
6336
|
const isTopLevelDocusaurus = platform === "docusaurus" && docusaurusProjectRoot && !options.docusaurusSkipRedirects;
|
|
5090
|
-
const
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
6337
|
+
const docusaurusSettings = isTopLevelDocusaurus ? readDocusaurusSiteSettings(docusaurusProjectRoot) : void 0;
|
|
6338
|
+
if (docusaurusSettings) {
|
|
6339
|
+
const importedRoutes = new Set(pages.map((page) => page.navigationId));
|
|
6340
|
+
const sourceOrigin = readDocusaurusSiteOrigin(docusaurusProjectRoot);
|
|
6341
|
+
const externalized = /* @__PURE__ */ new Set();
|
|
6342
|
+
const projectLink = (raw, sourceLiteral = false) => {
|
|
6343
|
+
if (/^https?:\/\//i.test(raw)) return raw;
|
|
6344
|
+
if (/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(raw) || raw.includes("\\")) return void 0;
|
|
6345
|
+
const suffixAt = raw.search(/[?#]/);
|
|
6346
|
+
const path = suffixAt < 0 ? raw : raw.slice(0, suffixAt);
|
|
6347
|
+
const suffix = suffixAt < 0 ? "" : raw.slice(suffixAt);
|
|
6348
|
+
const route = path.replace(/^\.?\//, "").replace(/\/+$/, "");
|
|
6349
|
+
if (route === ".." || route.startsWith("../") || route.includes("/../")) return void 0;
|
|
6350
|
+
const docsBase = docusaurusSettings.docsRouteBasePath.replace(/^\/+|\/+$/g, "");
|
|
6351
|
+
const isOutsideDocs = sourceLiteral && docsBase && route !== docsBase && !route.startsWith(`${docsBase}/`);
|
|
6352
|
+
if (isOutsideDocs && sourceOrigin) {
|
|
6353
|
+
externalized.add(raw);
|
|
6354
|
+
return new URL(raw, `${sourceOrigin}/`).toString();
|
|
6355
|
+
}
|
|
6356
|
+
const strippedDocsRoute = docsBase && route.startsWith(`${docsBase}/`) ? route.slice(docsBase.length + 1) : route;
|
|
6357
|
+
const candidates = [route, strippedDocsRoute, ...route === docsBase ? ["introduction"] : []];
|
|
6358
|
+
const imported = candidates.find((candidate) => importedRoutes.has(candidate));
|
|
6359
|
+
if (imported) return `/${imported}${suffix}`;
|
|
6360
|
+
if (sourceOrigin) {
|
|
6361
|
+
externalized.add(raw);
|
|
6362
|
+
return new URL(raw, `${sourceOrigin}/`).toString();
|
|
6363
|
+
}
|
|
6364
|
+
return path.startsWith("/") ? `${path}${suffix}` : `/${route}${suffix}`;
|
|
6365
|
+
};
|
|
6366
|
+
const firstNavigationPage = (nodes) => {
|
|
6367
|
+
for (const node of nodes) {
|
|
6368
|
+
if (typeof node === "string") return node;
|
|
6369
|
+
const nested = firstNavigationPage(node.pages);
|
|
6370
|
+
if (nested) return nested;
|
|
6371
|
+
}
|
|
6372
|
+
return void 0;
|
|
6373
|
+
};
|
|
6374
|
+
const navbarLinks = docusaurusSettings.navbarLinks.flatMap((link) => {
|
|
6375
|
+
const docRoute = link.docId && !link.docsPluginId && docusaurusDescriptors.find((page) => page.docId === link.docId)?.navigationId;
|
|
6376
|
+
const sidebar = link.sidebarId && docsConfig.tabs.find((tab) => tab.tab.toLowerCase() === link.sidebarId.toLowerCase());
|
|
6377
|
+
const sidebarRoute = sidebar && firstNavigationPage(sidebar.pages ?? sidebar.groups ?? []);
|
|
6378
|
+
const pluginRoot = link.docsPluginId && importedRoutes.has(link.docsPluginId) ? link.docsPluginId : void 0;
|
|
6379
|
+
const rawHref = link.href ?? (docRoute ? `/${docRoute}` : void 0) ?? (pluginRoot ? `/${pluginRoot}` : void 0) ?? (sidebarRoute ? `/${sidebarRoute}` : void 0) ?? (link.docsPluginId ? `/${link.docsPluginId}` : void 0);
|
|
6380
|
+
const href = rawHref && projectLink(rawHref, Boolean(link.href));
|
|
6381
|
+
return href ? [{ label: link.label, href }] : [];
|
|
6382
|
+
});
|
|
6383
|
+
const footerLinks = docusaurusSettings.footerLinks?.flatMap((column) => {
|
|
6384
|
+
const items = column.items.flatMap((item) => {
|
|
6385
|
+
const href = projectLink(item.href, true);
|
|
6386
|
+
return href ? [{ label: item.label, href }] : [];
|
|
6387
|
+
});
|
|
6388
|
+
return items.length > 0 ? [{ heading: column.heading, items }] : [];
|
|
6389
|
+
});
|
|
6390
|
+
docsConfig = {
|
|
6391
|
+
...docsConfig,
|
|
6392
|
+
...navbarLinks.length > 0 ? { navbar: { ...docsConfig.navbar, links: navbarLinks } } : {},
|
|
6393
|
+
...footerLinks?.length || docusaurusSettings.copyright ? {
|
|
6394
|
+
footer: {
|
|
6395
|
+
...docsConfig.footer,
|
|
6396
|
+
...footerLinks?.length ? { links: footerLinks } : {},
|
|
6397
|
+
...docusaurusSettings.copyright ? { copyright: docusaurusSettings.copyright } : {}
|
|
6398
|
+
}
|
|
6399
|
+
} : {}
|
|
6400
|
+
};
|
|
6401
|
+
if (externalized.size > 0) warnings.push({
|
|
5094
6402
|
code: "unsupported-config",
|
|
5095
|
-
message:
|
|
6403
|
+
message: `${externalized.size} site navigation/footer link(s) point outside the imported docs and were kept on ${sourceOrigin}.`
|
|
5096
6404
|
});
|
|
5097
6405
|
}
|
|
6406
|
+
if (!docsConfig.navigation?.display && docsConfig.tabs.filter((tab) => !tab.hidden).length > 6) {
|
|
6407
|
+
docsConfig = { ...docsConfig, navigation: { display: "dropdown" } };
|
|
6408
|
+
}
|
|
6409
|
+
const themeColors = mintlifyConfig ? mintlifyThemeColors(mintlifyConfig.colors) : fernRawConfig ? fernThemeColors(fernRawConfig.colors) : isTopLevelDocusaurus ? readDocusaurusThemeColor(docusaurusProjectRoot) : void 0;
|
|
6410
|
+
const assetPaths = new Set(assets.map((asset) => asset.path));
|
|
6411
|
+
const publicBrandPath = (value) => {
|
|
6412
|
+
if (typeof value !== "string" || /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(value)) return void 0;
|
|
6413
|
+
const normalized = normalizeAssetPath(value);
|
|
6414
|
+
return normalized && assetPaths.has(normalized) ? `/${normalized}` : void 0;
|
|
6415
|
+
};
|
|
6416
|
+
const brandVariants = (value) => {
|
|
6417
|
+
if (typeof value === "string") return { light: value };
|
|
6418
|
+
if (!value || typeof value !== "object") return {};
|
|
6419
|
+
const source = value;
|
|
6420
|
+
const light = source.light ?? source.src;
|
|
6421
|
+
const dark = source.dark ?? source.srcDark;
|
|
6422
|
+
return {
|
|
6423
|
+
...typeof light === "string" ? { light } : {},
|
|
6424
|
+
...typeof dark === "string" ? { dark } : {},
|
|
6425
|
+
...typeof source.showTitle === "boolean" ? { showTitle: source.showTitle } : {}
|
|
6426
|
+
};
|
|
6427
|
+
};
|
|
6428
|
+
const sourceLogo = mintlifyConfig?.logo ?? fernRawConfig?.logo ?? docusaurusSettings?.logo;
|
|
6429
|
+
const sourceFavicon = mintlifyConfig?.favicon ?? fernRawConfig?.favicon ?? docusaurusSettings?.favicon;
|
|
6430
|
+
const logo = brandVariants(sourceLogo);
|
|
6431
|
+
const favicon = brandVariants(sourceFavicon);
|
|
6432
|
+
const logoLight = publicBrandPath(logo.light);
|
|
6433
|
+
const logoDark = publicBrandPath(logo.dark);
|
|
6434
|
+
const faviconLight = publicBrandPath(favicon.light);
|
|
6435
|
+
const faviconDark = publicBrandPath(favicon.dark);
|
|
6436
|
+
if (logoLight) docsConfig = {
|
|
6437
|
+
...docsConfig,
|
|
6438
|
+
navbar: {
|
|
6439
|
+
...docsConfig.navbar,
|
|
6440
|
+
logo: {
|
|
6441
|
+
light: logoLight,
|
|
6442
|
+
...logoDark ? { dark: logoDark } : {},
|
|
6443
|
+
showTitle: mintlifyConfig || fernRawConfig ? false : logo.showTitle !== false,
|
|
6444
|
+
...typeof fernRawConfig?.logo?.["right-text"] === "string" ? { rightText: String(fernRawConfig.logo["right-text"]).trim().slice(0, 40) } : {}
|
|
6445
|
+
}
|
|
6446
|
+
}
|
|
6447
|
+
};
|
|
6448
|
+
if (faviconLight) docsConfig = {
|
|
6449
|
+
...docsConfig,
|
|
6450
|
+
favicon: { light: faviconLight, ...faviconDark ? { dark: faviconDark } : {} }
|
|
6451
|
+
};
|
|
6452
|
+
const missingBrand = [
|
|
6453
|
+
...[logo.light, logo.dark].filter((value) => typeof value === "string" && !publicBrandPath(value)),
|
|
6454
|
+
...[favicon.light, favicon.dark].filter((value) => typeof value === "string" && !publicBrandPath(value))
|
|
6455
|
+
];
|
|
6456
|
+
if (missingBrand.length > 0) warnings.push({
|
|
6457
|
+
code: "unsupported-config",
|
|
6458
|
+
message: `Brand asset(s) were referenced but not imported: ${[...new Set(missingBrand)].join(", ")}. Add these files under public/ or update docs.json.`
|
|
6459
|
+
});
|
|
6460
|
+
if (fernRawConfig?.logo && !logoLight && !logo.light) warnings.push({
|
|
6461
|
+
code: "unsupported-config",
|
|
6462
|
+
message: "Fern supplied a logo through its global theme without a local asset; add a logo path to docs.json after import."
|
|
6463
|
+
});
|
|
5098
6464
|
return {
|
|
5099
6465
|
sourceUrl: options.sourceUrl,
|
|
5100
6466
|
sourceKind: "repository",
|
|
@@ -5114,6 +6480,12 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
5114
6480
|
...typeof fernRawConfig.title === "string" ? { name: fernRawConfig.title } : {},
|
|
5115
6481
|
...themeColors ? { colors: themeColors } : {}
|
|
5116
6482
|
}
|
|
6483
|
+
} : docusaurusSettings && (docusaurusSettings.name || docusaurusSettings.description || themeColors) ? {
|
|
6484
|
+
site: {
|
|
6485
|
+
...docusaurusSettings.name ? { name: docusaurusSettings.name } : {},
|
|
6486
|
+
...docusaurusSettings.description ? { description: docusaurusSettings.description } : {},
|
|
6487
|
+
...themeColors ? { colors: themeColors } : {}
|
|
6488
|
+
}
|
|
5117
6489
|
} : themeColors ? {
|
|
5118
6490
|
site: { colors: themeColors }
|
|
5119
6491
|
} : {},
|
|
@@ -5132,6 +6504,7 @@ function renderPage(bundle, page) {
|
|
|
5132
6504
|
`title: ${yamlString(page.title)}`,
|
|
5133
6505
|
page.navTitle ? `navTitle: ${yamlString(page.navTitle)}` : null,
|
|
5134
6506
|
`description: ${yamlString(page.description)}`,
|
|
6507
|
+
page.descriptionPlacement === "body" ? "descriptionPlacement: body" : null,
|
|
5135
6508
|
page.badge ? `badge: ${yamlString(page.badge)}` : null,
|
|
5136
6509
|
page.keywords.length > 0 ? `keywords: [${page.keywords.map(yamlString).join(", ")}]` : null,
|
|
5137
6510
|
page.mode ? `mode: ${yamlString(page.mode)}` : null,
|
|
@@ -5233,7 +6606,7 @@ function renderMigrationFiles(bundle, options = {}) {
|
|
|
5233
6606
|
}
|
|
5234
6607
|
|
|
5235
6608
|
// src/url.ts
|
|
5236
|
-
import { load } from "cheerio";
|
|
6609
|
+
import { load as load2 } from "cheerio";
|
|
5237
6610
|
import remarkMdx3 from "remark-mdx";
|
|
5238
6611
|
import remarkParse3 from "remark-parse";
|
|
5239
6612
|
import TurndownService from "turndown";
|
|
@@ -5410,7 +6783,7 @@ function machineIndexScopePath(source, documents) {
|
|
|
5410
6783
|
if (!document) continue;
|
|
5411
6784
|
const references = [...headerLinks(document)];
|
|
5412
6785
|
if (/html/i.test(document.contentType)) {
|
|
5413
|
-
const $ =
|
|
6786
|
+
const $ = load2(document.body);
|
|
5414
6787
|
references.push(...$("link[href], a[href]").map((_index, element) => $(element).attr("href") ?? "").get().filter((href) => /\/(?:llms(?:-full)?\.txt|sitemap\.xml)(?:[?#]|$)/i.test(href)));
|
|
5415
6788
|
}
|
|
5416
6789
|
for (const reference of references) {
|
|
@@ -5521,7 +6894,7 @@ function jsonObjectAfterKey(value, key) {
|
|
|
5521
6894
|
return null;
|
|
5522
6895
|
}
|
|
5523
6896
|
function embeddedMintlifyConfig(body) {
|
|
5524
|
-
const $ =
|
|
6897
|
+
const $ = load2(body);
|
|
5525
6898
|
const flightPayloads = [];
|
|
5526
6899
|
$("script").each((_index, element) => {
|
|
5527
6900
|
const script = $(element).text();
|
|
@@ -5619,7 +6992,7 @@ function isSafePortableUrl(value) {
|
|
|
5619
6992
|
}
|
|
5620
6993
|
}
|
|
5621
6994
|
function decodeHtmlAttribute(value) {
|
|
5622
|
-
const document =
|
|
6995
|
+
const document = load2(`<i data-thally-value="${value.replace(/"/g, """)}"></i>`);
|
|
5623
6996
|
return document("i").attr("data-thally-value") ?? "";
|
|
5624
6997
|
}
|
|
5625
6998
|
function escapeMarkdownText(value, escapeTableSeparator = false) {
|
|
@@ -5668,7 +7041,7 @@ function replacePortableHtmlImages(value) {
|
|
|
5668
7041
|
if (start < cursor) continue;
|
|
5669
7042
|
const end = htmlOpeningTagEnd(value, start + match[0].length);
|
|
5670
7043
|
if (end === null) continue;
|
|
5671
|
-
const fragment =
|
|
7044
|
+
const fragment = load2(value.slice(start, end));
|
|
5672
7045
|
const image = fragment("img").first();
|
|
5673
7046
|
const source = image.attr("src");
|
|
5674
7047
|
const destination = source && isSafePortableUrl(source) ? markdownLinkDestination(source) : null;
|
|
@@ -5693,7 +7066,7 @@ function replacePortableHtmlAnchors(value) {
|
|
|
5693
7066
|
const closing = closingMatch.exec(value);
|
|
5694
7067
|
if (!closing) continue;
|
|
5695
7068
|
const end = closing.index + closing[0].length;
|
|
5696
|
-
const fragment =
|
|
7069
|
+
const fragment = load2(value.slice(start, end));
|
|
5697
7070
|
const anchor = fragment("a").first();
|
|
5698
7071
|
const href = anchor.attr("href");
|
|
5699
7072
|
const label = escapeMarkdownText(anchor.text().replace(/\s+/g, " ").trim());
|
|
@@ -6074,7 +7447,7 @@ function mergeEmbeddedOpenApi(fragments) {
|
|
|
6074
7447
|
return new TextEncoder().encode(stringifyYaml(combined));
|
|
6075
7448
|
}
|
|
6076
7449
|
function htmlPage(document, id) {
|
|
6077
|
-
const $ =
|
|
7450
|
+
const $ = load2(document.body);
|
|
6078
7451
|
const links = $('nav a[href], main a[href], article a[href], [role="main"] a[href]').map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
|
|
6079
7452
|
const title = $("main h1, article h1, h1").first().text().trim() || $('meta[property="og:title"]').attr("content")?.trim() || $("title").text().split("|")[0].trim();
|
|
6080
7453
|
const description = $('meta[name="description"]').attr("content")?.trim() || $('meta[property="og:description"]').attr("content")?.trim();
|
|
@@ -6118,7 +7491,7 @@ function htmlPage(document, id) {
|
|
|
6118
7491
|
const element = node;
|
|
6119
7492
|
const className = element.className;
|
|
6120
7493
|
const tag = /admonition-(?:danger)/.test(className) ? "Error" : /admonition-(?:caution|warning)/.test(className) ? "Warning" : /admonition-info/.test(className) ? "Info" : "Note";
|
|
6121
|
-
const fragment =
|
|
7494
|
+
const fragment = load2(element.outerHTML);
|
|
6122
7495
|
const clone = fragment(".theme-admonition").first();
|
|
6123
7496
|
const title2 = clone.find('[class*="admonitionHeading"],.admonition-heading').first();
|
|
6124
7497
|
const titleText = title2.text().trim();
|
|
@@ -6139,7 +7512,7 @@ ${titleText ? `**${titleText}**
|
|
|
6139
7512
|
filter: (node) => node.nodeName === "DIV" && node.classList.contains("tabs-container"),
|
|
6140
7513
|
replacement: (_content, node) => {
|
|
6141
7514
|
const element = node;
|
|
6142
|
-
const fragment =
|
|
7515
|
+
const fragment = load2(element.outerHTML);
|
|
6143
7516
|
const container = fragment(".tabs-container").first();
|
|
6144
7517
|
const labels = container.children("ul.tabs").children("li").toArray().map((item) => fragment(item).text().trim() || "Tab");
|
|
6145
7518
|
const panels = container.children('div[role="tabpanel"]').toArray();
|
|
@@ -6162,7 +7535,7 @@ ${tabs.join("\n\n")}
|
|
|
6162
7535
|
turndown.addRule("gfm-table", {
|
|
6163
7536
|
filter: "table",
|
|
6164
7537
|
replacement: (_content, node) => {
|
|
6165
|
-
const fragment =
|
|
7538
|
+
const fragment = load2(node.outerHTML);
|
|
6166
7539
|
const rows = fragment("tr").toArray().map((row) => fragment(row).children("th,td").toArray().map((cell) => escapeMarkdownText(
|
|
6167
7540
|
fragment(cell).text().replace(/\s+/g, " ").trim(),
|
|
6168
7541
|
true
|
|
@@ -6183,7 +7556,7 @@ ${normalized.slice(1).map((row) => `| ${row.join(" | ")} |`).join("\n")}
|
|
|
6183
7556
|
filter: (node) => node.nodeName === "PRE",
|
|
6184
7557
|
replacement: (_content, node) => {
|
|
6185
7558
|
const element = node;
|
|
6186
|
-
const fragment =
|
|
7559
|
+
const fragment = load2(element.outerHTML);
|
|
6187
7560
|
const tokenLines = fragment(".token-line").toArray();
|
|
6188
7561
|
const code = (tokenLines.length > 0 ? tokenLines.map((line) => fragment(line).text()).join("\n") : fragment("pre").text()).replace(/\n$/, "");
|
|
6189
7562
|
const language = fragment("code").attr("class")?.match(/(?:language-|lang-)([A-Za-z0-9_+-]+)/)?.[1] ?? "";
|
|
@@ -6216,7 +7589,7 @@ ${fence}
|
|
|
6216
7589
|
};
|
|
6217
7590
|
}
|
|
6218
7591
|
function htmlNavigationLinks(document) {
|
|
6219
|
-
const $ =
|
|
7592
|
+
const $ = load2(document.body);
|
|
6220
7593
|
return $('.nav-tabs a[href], nav a[href], aside a[href], [role="navigation"] a[href]').map((_index, element) => $(element).attr("href") ?? "").get().filter(Boolean);
|
|
6221
7594
|
}
|
|
6222
7595
|
function normalizedLinkLabel(value) {
|
|
@@ -6224,7 +7597,7 @@ function normalizedLinkLabel(value) {
|
|
|
6224
7597
|
}
|
|
6225
7598
|
function readDocusaurusSidebar(document) {
|
|
6226
7599
|
if (!/html/i.test(document.contentType)) return null;
|
|
6227
|
-
const $ =
|
|
7600
|
+
const $ = load2(document.body);
|
|
6228
7601
|
const sidebar = $("ul.theme-doc-sidebar-menu").first();
|
|
6229
7602
|
if (sidebar.length === 0) return null;
|
|
6230
7603
|
function readList(list) {
|
|
@@ -6260,7 +7633,7 @@ function docusaurusSidebarHrefs(snapshot) {
|
|
|
6260
7633
|
return hrefs;
|
|
6261
7634
|
}
|
|
6262
7635
|
function deriveDocusaurusScope(source, document, submittedScopePath) {
|
|
6263
|
-
const $ =
|
|
7636
|
+
const $ = load2(document.body);
|
|
6264
7637
|
const sidebar = readDocusaurusSidebar(document);
|
|
6265
7638
|
const internalPaths = docusaurusSidebarHrefs(sidebar).flatMap((href) => {
|
|
6266
7639
|
try {
|
|
@@ -6390,7 +7763,7 @@ function docusaurusNavigationFromSnapshots(snapshots, pages, scopePath) {
|
|
|
6390
7763
|
return { tabs };
|
|
6391
7764
|
}
|
|
6392
7765
|
function htmlTopLevelNavigation(document, source, scopePath) {
|
|
6393
|
-
const $ =
|
|
7766
|
+
const $ = load2(document.body);
|
|
6394
7767
|
const collectCandidates = (selector) => {
|
|
6395
7768
|
const candidates2 = [];
|
|
6396
7769
|
$(selector).each((_containerIndex, container) => {
|
|
@@ -6451,7 +7824,7 @@ function markdownLinks(body) {
|
|
|
6451
7824
|
return [...body.matchAll(/\[[^\]]+\]\(([^)\s]+)(?:\s+['"][^'"]*['"])?\)/g)].map((match) => match[1]);
|
|
6452
7825
|
}
|
|
6453
7826
|
function sitemapLinks(body) {
|
|
6454
|
-
const $ =
|
|
7827
|
+
const $ = load2(body, { xmlMode: true });
|
|
6455
7828
|
return $("url > loc, sitemap > loc").map((_index, element) => $(element).text().trim()).get().filter(Boolean);
|
|
6456
7829
|
}
|
|
6457
7830
|
function robotsSitemaps(body) {
|
|
@@ -6490,7 +7863,7 @@ async function migrateUrl(options) {
|
|
|
6490
7863
|
const platform = options.platform ?? detectUrlPlatform(scopedHtmlProbe ?? initial);
|
|
6491
7864
|
let docusaurusHtmlProbe = platform === "docusaurus" ? scopedHtmlProbe : null;
|
|
6492
7865
|
if (docusaurusHtmlProbe && !readDocusaurusSidebar(docusaurusHtmlProbe)) {
|
|
6493
|
-
const $ =
|
|
7866
|
+
const $ = load2(docusaurusHtmlProbe.body);
|
|
6494
7867
|
const docsHref = $("nav a[href]").toArray().map((element) => ({
|
|
6495
7868
|
href: $(element).attr("href"),
|
|
6496
7869
|
label: normalizedLinkLabel($(element).text())
|