@thallylabs/migrate 0.2.1 → 0.2.2
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 +13 -4
- package/dist/index.d.ts +71 -3
- package/dist/index.js +624 -85
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { spawn } from "child_process";
|
|
3
3
|
import {
|
|
4
4
|
existsSync as existsSync3,
|
|
5
|
-
lstatSync as
|
|
5
|
+
lstatSync as lstatSync3,
|
|
6
6
|
readFileSync as readFileSync3,
|
|
7
7
|
readdirSync
|
|
8
8
|
} from "fs";
|
|
@@ -16,16 +16,29 @@ import { parse as parseYaml } from "yaml";
|
|
|
16
16
|
|
|
17
17
|
// src/path.ts
|
|
18
18
|
import { isAbsolute, relative, resolve, sep } from "path";
|
|
19
|
-
var SAFE_SEGMENT = /[^a-z0-9._-]+/
|
|
20
|
-
function
|
|
19
|
+
var SAFE_SEGMENT = /[^a-z0-9._-]+/gi;
|
|
20
|
+
function trimTrailingSlashes(value) {
|
|
21
|
+
let end = value.length;
|
|
22
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) end--;
|
|
23
|
+
return value.slice(0, end);
|
|
24
|
+
}
|
|
25
|
+
function trimEdgeSlashes(value) {
|
|
26
|
+
let start = 0;
|
|
27
|
+
let end = value.length;
|
|
28
|
+
while (start < end && value.charCodeAt(start) === 47) start++;
|
|
29
|
+
while (end > start && value.charCodeAt(end - 1) === 47) end--;
|
|
30
|
+
return value.slice(start, end);
|
|
31
|
+
}
|
|
32
|
+
function slugifySegment(value, preserveCase = false) {
|
|
21
33
|
let decoded = value;
|
|
22
34
|
try {
|
|
23
35
|
decoded = decodeURIComponent(value);
|
|
24
36
|
} catch {
|
|
25
37
|
}
|
|
26
|
-
|
|
38
|
+
const normalized = preserveCase ? decoded : decoded.toLowerCase();
|
|
39
|
+
return normalized.replace(/\.(?:html?|mdx?)$/i, "").replace(SAFE_SEGMENT, "-").replace(/(^-|-$)/g, "");
|
|
27
40
|
}
|
|
28
|
-
function pageIdFromReference(value) {
|
|
41
|
+
function pageIdFromReference(value, preserveCase = false) {
|
|
29
42
|
const withoutQuery = value.split(/[?#]/, 1)[0].replace(/\\/g, "/").replace(/^\/+/, "");
|
|
30
43
|
if (!withoutQuery || withoutQuery.includes("\0")) return null;
|
|
31
44
|
const rawSegments = withoutQuery.split("/").filter(Boolean);
|
|
@@ -35,7 +48,7 @@ function pageIdFromReference(value) {
|
|
|
35
48
|
if (!/^(?:index|readme)$/i.test(last)) {
|
|
36
49
|
baseSegments[baseSegments.length - 1] = last;
|
|
37
50
|
}
|
|
38
|
-
const segments = baseSegments.map(slugifySegment).filter(Boolean);
|
|
51
|
+
const segments = baseSegments.map((segment) => slugifySegment(segment, preserveCase)).filter(Boolean);
|
|
39
52
|
return segments.join("/") || "introduction";
|
|
40
53
|
}
|
|
41
54
|
function resolveWithin(root, candidate) {
|
|
@@ -585,7 +598,7 @@ function normalizeMdx(body) {
|
|
|
585
598
|
return normalizeDocusaurusAdmonitions(body).replace(/^import\s+(?:Tabs|TabItem|Link|DocCardList|TOCInline)\s+from\s+['"]@(?:theme|docusaurus)\/[^'"]+['"]\s*;?\s*$/gm, "").replace(/<TabItem\b([^>]*)>/g, (_match, attributes) => {
|
|
586
599
|
const title = attributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? attributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? "Tab";
|
|
587
600
|
return `<Tab title="${title.replace(/"/g, """)}">`;
|
|
588
|
-
}).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, "").replace(/<!--([\s\S]*?)-->/g, (_match, content) => `{/*${content}*/}`).replace(/<Danger(\s[^>]*)?>/g, "<Error$1>").replace(/<\/Danger>/g, "</Error>").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>");
|
|
601
|
+
}).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, "").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>");
|
|
589
602
|
}
|
|
590
603
|
function parseMarkdownPage(input) {
|
|
591
604
|
const parsed = parseFrontmatter(input.raw);
|
|
@@ -598,14 +611,23 @@ function parseMarkdownPage(input) {
|
|
|
598
611
|
const body = normalizeMdx(parsed.content).trim();
|
|
599
612
|
const keywords = Array.isArray(parsed.data.keywords) ? parsed.data.keywords.filter((value) => typeof value === "string") : [];
|
|
600
613
|
const title = typeof parsed.data.title === "string" && parsed.data.title.trim() ? parsed.data.title.trim() : titleFromId(identity.navigationId);
|
|
614
|
+
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;
|
|
615
|
+
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;
|
|
616
|
+
const sourceMode = typeof parsed.data.mode === "string" ? parsed.data.mode : void 0;
|
|
617
|
+
const mode = sourceMode === "frame" ? "wide" : ["default", "wide", "custom", "center", "home"].includes(sourceMode ?? "") ? sourceMode : void 0;
|
|
601
618
|
const description = typeof parsed.data.description === "string" && parsed.data.description.trim() ? parsed.data.description.trim() : firstParagraph(body);
|
|
602
619
|
return {
|
|
603
620
|
id: identity.id,
|
|
604
621
|
navigationId: identity.navigationId,
|
|
605
622
|
locale: identity.locale,
|
|
606
623
|
title,
|
|
624
|
+
navTitle,
|
|
607
625
|
description,
|
|
626
|
+
badge,
|
|
608
627
|
keywords,
|
|
628
|
+
mode,
|
|
629
|
+
hidden: parsed.data.hidden === true ? true : void 0,
|
|
630
|
+
noindex: parsed.data.noindex === true || parsed.data.noindex === "true" ? true : void 0,
|
|
609
631
|
openapi: typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0,
|
|
610
632
|
body,
|
|
611
633
|
source: input.source
|
|
@@ -613,8 +635,24 @@ function parseMarkdownPage(input) {
|
|
|
613
635
|
}
|
|
614
636
|
|
|
615
637
|
// src/navigation.ts
|
|
616
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
638
|
+
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
617
639
|
import { dirname, extname as extname2, relative as relative2 } from "path";
|
|
640
|
+
var MAX_MINTLIFY_CONFIG_BYTES = 2e6;
|
|
641
|
+
function addMintlifyDirectoryRedirects(config, pages) {
|
|
642
|
+
const pageIds = new Set(pages.filter((page) => !page.locale).map((page) => page.navigationId));
|
|
643
|
+
const redirects = [...config.redirects ?? []];
|
|
644
|
+
const redirectSources = new Set(redirects.map((redirect) => redirect.source.replace(/\/$/, "") || "/"));
|
|
645
|
+
for (const page of pages) {
|
|
646
|
+
if (page.locale || !/(?:^|\/)(?:overview|introduction)$/.test(page.navigationId)) continue;
|
|
647
|
+
const parent = page.navigationId.replace(/\/(?:overview|introduction)$/, "");
|
|
648
|
+
if (!parent || pageIds.has(parent)) continue;
|
|
649
|
+
const source = `/${parent}`;
|
|
650
|
+
if (redirectSources.has(source)) continue;
|
|
651
|
+
redirects.push({ source, destination: `/${page.navigationId}`, permanent: false });
|
|
652
|
+
redirectSources.add(source);
|
|
653
|
+
}
|
|
654
|
+
return redirects.length > 0 ? { ...config, redirects } : config;
|
|
655
|
+
}
|
|
618
656
|
var LANGUAGE_LABELS = {
|
|
619
657
|
ar: "Arabic",
|
|
620
658
|
de: "German",
|
|
@@ -636,6 +674,15 @@ var LANGUAGE_LABELS = {
|
|
|
636
674
|
function objectValue2(value) {
|
|
637
675
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
638
676
|
}
|
|
677
|
+
function projectedHref(value) {
|
|
678
|
+
if (typeof value !== "string") return void 0;
|
|
679
|
+
const href = value.trim();
|
|
680
|
+
if (!href || /[\u0000-\u001f\u007f]/.test(href)) return void 0;
|
|
681
|
+
if (/^(?:javascript|data|vbscript|file):/i.test(href)) return void 0;
|
|
682
|
+
const scheme = href.match(/^([a-z][a-z0-9+.-]*):/i)?.[1].toLowerCase();
|
|
683
|
+
if (scheme && !["http", "https", "mailto", "tel"].includes(scheme)) return void 0;
|
|
684
|
+
return href;
|
|
685
|
+
}
|
|
639
686
|
function labelFor(value, fallback) {
|
|
640
687
|
for (const key of ["tab", "group", "anchor", "product", "dropdown", "version", "menu", "label", "name", "title"]) {
|
|
641
688
|
if (typeof value[key] === "string" && value[key]) return String(value[key]);
|
|
@@ -649,11 +696,27 @@ function jsonPointer(root, pointer) {
|
|
|
649
696
|
if (!pointer || pointer === "#") return root;
|
|
650
697
|
const tokens = pointer.replace(/^#\/?/, "").split("/").filter(Boolean);
|
|
651
698
|
return tokens.reduce((current, token) => {
|
|
699
|
+
if (Array.isArray(current)) {
|
|
700
|
+
const index = Number(token);
|
|
701
|
+
return Number.isSafeInteger(index) && index >= 0 ? current[index] : void 0;
|
|
702
|
+
}
|
|
652
703
|
const object = objectValue2(current);
|
|
653
704
|
if (!object) return void 0;
|
|
654
705
|
return object[token.replace(/~1/g, "/").replace(/~0/g, "~")];
|
|
655
706
|
}, root);
|
|
656
707
|
}
|
|
708
|
+
function readMintlifyJson(path, repositoryRoot) {
|
|
709
|
+
if (!existsSync2(path) || !lstatSync2(path).isFile()) {
|
|
710
|
+
throw new Error(`Mintlify config reference is not a regular file: ${relative2(repositoryRoot, path)}`);
|
|
711
|
+
}
|
|
712
|
+
const realPath = realpathSync(path);
|
|
713
|
+
const realRoot = realpathSync(repositoryRoot);
|
|
714
|
+
resolveWithin(realRoot, relative2(realRoot, realPath));
|
|
715
|
+
if (lstatSync2(realPath).size > MAX_MINTLIFY_CONFIG_BYTES) {
|
|
716
|
+
throw new Error(`Mintlify config reference exceeded ${MAX_MINTLIFY_CONFIG_BYTES / 1e6} MB: ${relative2(repositoryRoot, path)}`);
|
|
717
|
+
}
|
|
718
|
+
return JSON.parse(readFileSync2(realPath, "utf8"));
|
|
719
|
+
}
|
|
657
720
|
function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
658
721
|
if (Array.isArray(value)) {
|
|
659
722
|
return value.map((entry) => resolveJsonReferences(entry, currentFile, repositoryRoot, stack));
|
|
@@ -668,11 +731,17 @@ function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
|
668
731
|
const stackKey = `${referencedFile}#${pointer}`;
|
|
669
732
|
if (stack.has(stackKey)) throw new Error(`Circular Mintlify $ref: ${object.$ref}`);
|
|
670
733
|
stack.add(stackKey);
|
|
671
|
-
const referencedRoot =
|
|
734
|
+
const referencedRoot = readMintlifyJson(referencedFile, repositoryRoot);
|
|
672
735
|
const referenced = jsonPointer(referencedRoot, pointer ? `#${pointer}` : "");
|
|
673
736
|
const resolved = resolveJsonReferences(referenced, referencedFile, repositoryRoot, stack);
|
|
674
737
|
stack.delete(stackKey);
|
|
675
|
-
|
|
738
|
+
const siblings = Object.fromEntries(
|
|
739
|
+
Object.entries(object).filter(([key]) => key !== "$ref").map(([key, entry]) => [
|
|
740
|
+
key,
|
|
741
|
+
resolveJsonReferences(entry, currentFile, repositoryRoot, stack)
|
|
742
|
+
])
|
|
743
|
+
);
|
|
744
|
+
return objectValue2(resolved) ? { ...resolved, ...siblings } : resolved;
|
|
676
745
|
}
|
|
677
746
|
return Object.fromEntries(
|
|
678
747
|
Object.entries(object).map(([key, entry]) => [
|
|
@@ -684,18 +753,33 @@ function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
|
684
753
|
function readMintlifyConfig(repositoryRoot) {
|
|
685
754
|
const configPath = ["docs.json", "mint.json"].map((filename) => resolveWithin(repositoryRoot, filename)).find(existsSync2);
|
|
686
755
|
if (!configPath) return null;
|
|
687
|
-
const raw =
|
|
756
|
+
const raw = readMintlifyJson(configPath, repositoryRoot);
|
|
688
757
|
return resolveJsonReferences(raw, configPath, repositoryRoot, /* @__PURE__ */ new Set());
|
|
689
758
|
}
|
|
690
|
-
function normalizePageRef(value) {
|
|
759
|
+
function normalizePageRef(value, pathPrefix = "") {
|
|
691
760
|
if (/^(?:https?:)?\/\//i.test(value) || value.startsWith("#")) return null;
|
|
692
|
-
|
|
693
|
-
|
|
761
|
+
let ref = localReference(value).split("?", 1)[0].replace(/^\/+/, "");
|
|
762
|
+
const prefix = trimEdgeSlashes(pathPrefix);
|
|
763
|
+
if (prefix && (ref === prefix || ref.startsWith(`${prefix}/`))) {
|
|
764
|
+
ref = ref === prefix ? "introduction" : ref.slice(prefix.length + 1);
|
|
765
|
+
}
|
|
766
|
+
ref = ref.replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
767
|
+
return pageIdFromReference(ref, true);
|
|
768
|
+
}
|
|
769
|
+
function warnOnce(context, key, message) {
|
|
770
|
+
if (context.warningKeys.has(key)) return;
|
|
771
|
+
context.warningKeys.add(key);
|
|
772
|
+
context.warnings.push({ code: "unsupported-config", message });
|
|
773
|
+
}
|
|
774
|
+
function iconName(value) {
|
|
775
|
+
if (typeof value === "string" && value) return value;
|
|
776
|
+
const icon = objectValue2(value);
|
|
777
|
+
return typeof icon?.name === "string" && icon.name ? icon.name : void 0;
|
|
694
778
|
}
|
|
695
779
|
function registerReference(value, context) {
|
|
696
780
|
const localePrefix = context.locale ? `${context.locale}/` : "";
|
|
697
781
|
const localizedValue = localePrefix && value.replace(/^\/+/, "").startsWith(localePrefix) ? value.replace(/^\/+/, "").slice(localePrefix.length) : value;
|
|
698
|
-
const navigationId = normalizePageRef(localizedValue);
|
|
782
|
+
const navigationId = normalizePageRef(localizedValue, context.pathPrefix);
|
|
699
783
|
if (!navigationId) return null;
|
|
700
784
|
const key = `${context.locale ?? ""}:${value}`;
|
|
701
785
|
if (!context.seenReferences.has(key)) {
|
|
@@ -709,6 +793,17 @@ function convertPage(value, context) {
|
|
|
709
793
|
const object = objectValue2(value);
|
|
710
794
|
if (!object) return null;
|
|
711
795
|
if (typeof object.page === "string") return registerReference(object.page, context);
|
|
796
|
+
const href = projectedHref(object.href);
|
|
797
|
+
if (href && !object.pages && !object.groups) {
|
|
798
|
+
const page = registerReference(href, context);
|
|
799
|
+
if (page) return page;
|
|
800
|
+
warnOnce(
|
|
801
|
+
context,
|
|
802
|
+
"external-page-link",
|
|
803
|
+
"External links nested inside a Mintlify sidebar cannot be represented as Thally pages and were omitted."
|
|
804
|
+
);
|
|
805
|
+
return null;
|
|
806
|
+
}
|
|
712
807
|
const pages = Array.isArray(object.pages) ? object.pages : [];
|
|
713
808
|
if ("group" in object || pages.length > 0) {
|
|
714
809
|
const children = [];
|
|
@@ -723,7 +818,8 @@ function convertPage(value, context) {
|
|
|
723
818
|
if (children.length === 0) return null;
|
|
724
819
|
return {
|
|
725
820
|
group: labelFor(object, "Documentation"),
|
|
726
|
-
...
|
|
821
|
+
...iconName(object.icon) ? { icon: iconName(object.icon) } : {},
|
|
822
|
+
...object.hidden === true ? { hidden: true } : {},
|
|
727
823
|
pages: children
|
|
728
824
|
};
|
|
729
825
|
}
|
|
@@ -750,12 +846,20 @@ function convertContainerToTabs(containerValue, context, fallbackTab) {
|
|
|
750
846
|
const object = objectValue2(value);
|
|
751
847
|
if (!object) return [];
|
|
752
848
|
const tab = labelFor(object, `${fallbackTab} ${index + 1}`);
|
|
753
|
-
|
|
754
|
-
|
|
849
|
+
const href = projectedHref(object.href);
|
|
850
|
+
if (href && !object.pages && !object.groups) {
|
|
851
|
+
return [{ tab, href, ...object.hidden === true ? { hidden: true } : {} }];
|
|
755
852
|
}
|
|
756
853
|
const nested = convertContainerToTabs(object, context, tab);
|
|
757
854
|
if (nested.length > 0) {
|
|
758
|
-
if (nested.length === 1)
|
|
855
|
+
if (nested.length === 1) {
|
|
856
|
+
return [{
|
|
857
|
+
...nested[0],
|
|
858
|
+
tab,
|
|
859
|
+
...href ? { href } : {},
|
|
860
|
+
...object.hidden === true ? { hidden: true } : {}
|
|
861
|
+
}];
|
|
862
|
+
}
|
|
759
863
|
return nested.map((item) => ({ ...item, tab: `${tab}: ${item.tab}` }));
|
|
760
864
|
}
|
|
761
865
|
return [];
|
|
@@ -768,38 +872,168 @@ function convertContainerToTabs(containerValue, context, fallbackTab) {
|
|
|
768
872
|
];
|
|
769
873
|
if (typeof container.root === "string") rawPages.unshift(container.root);
|
|
770
874
|
const groups = convertGroups(rawPages, context, "Overview");
|
|
771
|
-
return groups.length > 0 ? [{
|
|
875
|
+
return groups.length > 0 ? [{
|
|
876
|
+
tab: fallbackTab,
|
|
877
|
+
groups,
|
|
878
|
+
...projectedHref(container.href) ? { href: projectedHref(container.href) } : {},
|
|
879
|
+
...container.hidden === true ? { hidden: true } : {}
|
|
880
|
+
}] : [];
|
|
881
|
+
}
|
|
882
|
+
function projectedTheme(value) {
|
|
883
|
+
if (value === "maple") return "maple";
|
|
884
|
+
if (["aspen", "luma", "sequoia"].includes(String(value))) return "sharp";
|
|
885
|
+
if (["almond", "palm"].includes(String(value))) return "minimal";
|
|
886
|
+
return typeof value === "string" ? "default" : void 0;
|
|
887
|
+
}
|
|
888
|
+
function projectedNavbar(value) {
|
|
889
|
+
const navbar = objectValue2(value);
|
|
890
|
+
if (!navbar) return void 0;
|
|
891
|
+
const links = Array.isArray(navbar.links) ? navbar.links.flatMap((entry) => {
|
|
892
|
+
const link = objectValue2(entry);
|
|
893
|
+
const href = projectedHref(link?.href);
|
|
894
|
+
if (!link || !href) return [];
|
|
895
|
+
const label = typeof link.label === "string" ? link.label : typeof link.name === "string" ? link.name : "";
|
|
896
|
+
if (!label) return [];
|
|
897
|
+
return [{ label, href, ...link.type === "github" ? { type: "github" } : {} }];
|
|
898
|
+
}) : [];
|
|
899
|
+
const primaryValue = objectValue2(navbar.primary);
|
|
900
|
+
const primaryHref = projectedHref(primaryValue?.href);
|
|
901
|
+
const primary = primaryValue && primaryHref ? {
|
|
902
|
+
label: typeof primaryValue.label === "string" ? primaryValue.label : primaryValue.type === "github" ? "GitHub" : "Get started",
|
|
903
|
+
href: primaryHref
|
|
904
|
+
} : void 0;
|
|
905
|
+
return links.length > 0 || primary ? { ...links.length > 0 ? { links } : {}, ...primary ? { primary } : {} } : void 0;
|
|
906
|
+
}
|
|
907
|
+
function projectedGlobalNavigationLinks(value) {
|
|
908
|
+
const global = objectValue2(value);
|
|
909
|
+
if (!global) return [];
|
|
910
|
+
const links = [];
|
|
911
|
+
const visit = (entry) => {
|
|
912
|
+
if (Array.isArray(entry)) {
|
|
913
|
+
entry.forEach(visit);
|
|
914
|
+
return;
|
|
915
|
+
}
|
|
916
|
+
const item = objectValue2(entry);
|
|
917
|
+
if (!item) return;
|
|
918
|
+
const href = projectedHref(item.href);
|
|
919
|
+
if (href) {
|
|
920
|
+
const label = labelFor(item, "");
|
|
921
|
+
if (label) links.push({ label, href });
|
|
922
|
+
}
|
|
923
|
+
for (const key of ["tabs", "anchors", "dropdowns", "products", "versions", "languages", "menus"]) {
|
|
924
|
+
if (Array.isArray(item[key])) visit(item[key]);
|
|
925
|
+
}
|
|
926
|
+
};
|
|
927
|
+
visit(global);
|
|
928
|
+
return links;
|
|
929
|
+
}
|
|
930
|
+
function projectedFooter(value) {
|
|
931
|
+
const footer = objectValue2(value);
|
|
932
|
+
if (!footer) return void 0;
|
|
933
|
+
const socials = objectValue2(footer.socials);
|
|
934
|
+
const socialLinks = socials ? Object.fromEntries(Object.entries(socials).flatMap(([key, value2]) => {
|
|
935
|
+
const href = projectedHref(value2);
|
|
936
|
+
return href ? [[key, href]] : [];
|
|
937
|
+
})) : void 0;
|
|
938
|
+
const links = Array.isArray(footer.links) ? footer.links.flatMap((entry) => {
|
|
939
|
+
const column = objectValue2(entry);
|
|
940
|
+
if (!column || !Array.isArray(column.items)) return [];
|
|
941
|
+
const heading = typeof column.heading === "string" ? column.heading : typeof column.header === "string" ? column.header : "";
|
|
942
|
+
const items = column.items.flatMap((item) => {
|
|
943
|
+
const link = objectValue2(item);
|
|
944
|
+
const href = projectedHref(link?.href);
|
|
945
|
+
return link && typeof link.label === "string" && href ? [{ label: link.label, href }] : [];
|
|
946
|
+
});
|
|
947
|
+
return heading && items.length > 0 ? [{ heading, items }] : [];
|
|
948
|
+
}) : [];
|
|
949
|
+
return socialLinks && Object.keys(socialLinks).length > 0 || links.length > 0 ? { ...socialLinks && Object.keys(socialLinks).length > 0 ? { socials: socialLinks } : {}, ...links.length > 0 ? { links } : {} } : void 0;
|
|
950
|
+
}
|
|
951
|
+
function projectedFont(value) {
|
|
952
|
+
if (typeof value === "string" && value) return { family: value };
|
|
953
|
+
const font = objectValue2(value);
|
|
954
|
+
if (!font) return void 0;
|
|
955
|
+
const family = typeof font.family === "string" ? font.family : typeof font.name === "string" ? font.name : void 0;
|
|
956
|
+
if (!family) return void 0;
|
|
957
|
+
const weight = Array.isArray(font.weight) ? font.weight.map(String) : Array.isArray(font.weights) ? font.weights.map(String) : void 0;
|
|
958
|
+
return { family, ...weight && weight.length > 0 ? { weight } : {} };
|
|
959
|
+
}
|
|
960
|
+
function projectedCompatibleConfig(config) {
|
|
961
|
+
const banner = objectValue2(config.banner);
|
|
962
|
+
const bannerContent = banner && (typeof banner.content === "string" || objectValue2(banner.content)) ? banner.content : void 0;
|
|
963
|
+
const bannerType = banner && ["info", "warning", "critical"].includes(String(banner.type ?? banner.variant)) ? String(banner.type ?? banner.variant) : void 0;
|
|
964
|
+
const bannerColor = objectValue2(banner?.color);
|
|
965
|
+
const bodyFont = projectedFont(objectValue2(config.fonts)?.body ?? config.fonts);
|
|
966
|
+
const headingFont = projectedFont(objectValue2(config.fonts)?.heading);
|
|
967
|
+
const feedback = objectValue2(config.feedback);
|
|
968
|
+
const seo = objectValue2(config.seo);
|
|
969
|
+
const navbar = projectedNavbar(config.navbar);
|
|
970
|
+
const globalLinks = projectedGlobalNavigationLinks(objectValue2(config.navigation)?.global);
|
|
971
|
+
const navbarLinks = [...new Map(
|
|
972
|
+
[...navbar?.links ?? [], ...globalLinks].map((link) => [`${link.label}:${link.href}`, link])
|
|
973
|
+
).values()];
|
|
974
|
+
const projectedNavigation = navbarLinks.length > 0 || navbar?.primary ? { ...navbarLinks.length > 0 ? { links: navbarLinks } : {}, ...navbar?.primary ? { primary: navbar.primary } : {} } : void 0;
|
|
975
|
+
return {
|
|
976
|
+
...projectedTheme(config.theme) ? { theme: projectedTheme(config.theme) } : {},
|
|
977
|
+
...bannerContent ? {
|
|
978
|
+
banner: {
|
|
979
|
+
content: bannerContent,
|
|
980
|
+
...banner?.dismissible === false ? { dismissible: false } : {},
|
|
981
|
+
...typeof banner?.id === "string" ? { id: banner.id } : {},
|
|
982
|
+
...typeof banner?.revision === "string" ? { revision: banner.revision } : {},
|
|
983
|
+
...bannerType ? { type: bannerType } : {},
|
|
984
|
+
...bannerColor ? { color: {
|
|
985
|
+
...typeof bannerColor.light === "string" ? { light: bannerColor.light } : {},
|
|
986
|
+
...typeof bannerColor.dark === "string" ? { dark: bannerColor.dark } : {}
|
|
987
|
+
} } : {}
|
|
988
|
+
}
|
|
989
|
+
} : {},
|
|
990
|
+
...projectedNavigation ? { navbar: projectedNavigation } : {},
|
|
991
|
+
...projectedFooter(config.footer) ? { footer: projectedFooter(config.footer) } : {},
|
|
992
|
+
...bodyFont || headingFont ? { fonts: { ...bodyFont ? { body: bodyFont } : {}, ...headingFont ? { heading: headingFont } : {} } } : {},
|
|
993
|
+
...seo?.indexing === "all" ? { seo: { indexing: "all" } } : {},
|
|
994
|
+
...typeof feedback?.thumbsRating === "boolean" ? { feedback: { thumbsRating: feedback.thumbsRating } } : {}
|
|
995
|
+
};
|
|
772
996
|
}
|
|
773
|
-
function projectMintlifyNavigation(config) {
|
|
997
|
+
function projectMintlifyNavigation(config, options = {}) {
|
|
774
998
|
const warnings = [];
|
|
775
999
|
const references = [];
|
|
776
1000
|
const seenReferences = /* @__PURE__ */ new Set();
|
|
1001
|
+
const warningKeys = /* @__PURE__ */ new Set();
|
|
777
1002
|
const navigation = objectValue2(config.navigation) ?? config;
|
|
778
1003
|
const languages = Array.isArray(navigation.languages) ? navigation.languages.map(objectValue2).filter((value) => Boolean(value)) : [];
|
|
779
1004
|
let tabs = [];
|
|
780
1005
|
let i18n;
|
|
781
1006
|
if (languages.length > 0) {
|
|
782
|
-
const defaultLanguage = languages.find((entry) => entry.
|
|
1007
|
+
const defaultLanguage = languages.find((entry) => entry.default === true) ?? languages[0];
|
|
783
1008
|
const defaultLocale = String(defaultLanguage.language ?? "en");
|
|
784
1009
|
const locales = languages.map((entry) => {
|
|
785
1010
|
const code = String(entry.language ?? entry.locale ?? "en");
|
|
786
|
-
|
|
1011
|
+
let label = LANGUAGE_LABELS[code];
|
|
1012
|
+
try {
|
|
1013
|
+
label ??= new Intl.DisplayNames(["en"], { type: "language" }).of(code) ?? void 0;
|
|
1014
|
+
} catch {
|
|
1015
|
+
}
|
|
1016
|
+
return { code, label: label ?? code.toUpperCase() };
|
|
787
1017
|
});
|
|
788
1018
|
i18n = { defaultLocale, locales };
|
|
789
1019
|
for (const language of languages) {
|
|
790
1020
|
const locale = String(language.language ?? language.locale ?? defaultLocale);
|
|
791
1021
|
const context = {
|
|
792
1022
|
locale,
|
|
1023
|
+
pathPrefix: options.pathPrefix,
|
|
793
1024
|
references,
|
|
794
|
-
seenReferences
|
|
1025
|
+
seenReferences,
|
|
1026
|
+
warnings,
|
|
1027
|
+
warningKeys
|
|
795
1028
|
};
|
|
796
1029
|
const languageTabs = convertContainerToTabs(language, context, "Documentation");
|
|
797
1030
|
if (language === defaultLanguage) tabs = languageTabs;
|
|
798
1031
|
}
|
|
799
1032
|
} else {
|
|
800
|
-
|
|
1033
|
+
const context = { references, seenReferences, warnings, warningKeys, pathPrefix: options.pathPrefix };
|
|
1034
|
+
tabs = convertContainerToTabs(navigation, context, "Documentation");
|
|
801
1035
|
if (tabs.length === 0 && Array.isArray(config.navigation)) {
|
|
802
|
-
const groups = convertGroups(config.navigation,
|
|
1036
|
+
const groups = convertGroups(config.navigation, context, "Overview");
|
|
803
1037
|
if (groups.length > 0) tabs = [{ tab: "Documentation", groups }];
|
|
804
1038
|
}
|
|
805
1039
|
}
|
|
@@ -809,20 +1043,25 @@ function projectMintlifyNavigation(config) {
|
|
|
809
1043
|
message: "Mintlify navigation could not be projected; generated navigation will be used."
|
|
810
1044
|
});
|
|
811
1045
|
}
|
|
812
|
-
if (!tabs.some((tab) => tab.tab.toLowerCase() === "changelog")) {
|
|
813
|
-
tabs.push({ tab: "Changelog", href: "/changelog" });
|
|
814
|
-
}
|
|
815
1046
|
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
816
1047
|
const redirect = objectValue2(value);
|
|
817
1048
|
if (!redirect || typeof redirect.source !== "string" || typeof redirect.destination !== "string") return [];
|
|
1049
|
+
const source = redirect.source.trim();
|
|
1050
|
+
const destination = redirect.destination.trim();
|
|
1051
|
+
if (!source.startsWith("/") || !destination.startsWith("/") || source.startsWith("//") || destination.startsWith("//")) return [];
|
|
818
1052
|
return [{
|
|
819
|
-
source
|
|
820
|
-
destination
|
|
1053
|
+
source,
|
|
1054
|
+
destination,
|
|
821
1055
|
...typeof redirect.permanent === "boolean" ? { permanent: redirect.permanent } : {}
|
|
822
1056
|
}];
|
|
823
1057
|
}) : [];
|
|
824
1058
|
return {
|
|
825
|
-
docsConfig: {
|
|
1059
|
+
docsConfig: {
|
|
1060
|
+
tabs,
|
|
1061
|
+
...projectedCompatibleConfig(config),
|
|
1062
|
+
...i18n ? { i18n } : {},
|
|
1063
|
+
...redirects.length > 0 ? { redirects } : {}
|
|
1064
|
+
},
|
|
826
1065
|
pageReferences: references,
|
|
827
1066
|
warnings
|
|
828
1067
|
};
|
|
@@ -991,12 +1230,50 @@ var DOCUSAURUS_CONFIG_FILENAMES = [
|
|
|
991
1230
|
function hasDocusaurusConfig(directory) {
|
|
992
1231
|
return DOCUSAURUS_CONFIG_FILENAMES.some((filename) => {
|
|
993
1232
|
const path = resolveWithin(directory, filename);
|
|
994
|
-
return existsSync3(path) &&
|
|
1233
|
+
return existsSync3(path) && lstatSync3(path).isFile();
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
function hasMintlifyConfig(directory) {
|
|
1237
|
+
return ["docs.json", "mint.json"].some((filename) => {
|
|
1238
|
+
const path = resolveWithin(directory, filename);
|
|
1239
|
+
if (!existsSync3(path) || !lstatSync3(path).isFile()) return false;
|
|
1240
|
+
if (filename === "mint.json") return true;
|
|
1241
|
+
try {
|
|
1242
|
+
const config = JSON.parse(readFileSync3(path, "utf8"));
|
|
1243
|
+
return String(config.$schema ?? "").includes("mintlify") || "navigation" in config;
|
|
1244
|
+
} catch {
|
|
1245
|
+
return false;
|
|
1246
|
+
}
|
|
995
1247
|
});
|
|
996
1248
|
}
|
|
1249
|
+
function findMintlifyProjectRoot(repositoryDir, docsDir) {
|
|
1250
|
+
if (docsDir !== void 0) {
|
|
1251
|
+
let candidate = trimTrailingSlashes(docsDir);
|
|
1252
|
+
while (true) {
|
|
1253
|
+
const path = resolveWithin(repositoryDir, candidate || ".");
|
|
1254
|
+
if (hasMintlifyConfig(path)) return path;
|
|
1255
|
+
if (!candidate) break;
|
|
1256
|
+
const parent = dirname2(candidate);
|
|
1257
|
+
candidate = parent === "." ? "" : parent;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
const queue = [{ directory: repositoryDir, depth: 0 }];
|
|
1261
|
+
let visited = 0;
|
|
1262
|
+
while (queue.length > 0 && visited < 500) {
|
|
1263
|
+
const current = queue.shift();
|
|
1264
|
+
visited++;
|
|
1265
|
+
if (hasMintlifyConfig(current.directory)) return current.directory;
|
|
1266
|
+
if (current.depth >= 4) continue;
|
|
1267
|
+
for (const entry of readdirSync(current.directory, { withFileTypes: true })) {
|
|
1268
|
+
if (!entry.isDirectory() || entry.isSymbolicLink() || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
1269
|
+
queue.push({ directory: resolveWithin(current.directory, entry.name), depth: current.depth + 1 });
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
return null;
|
|
1273
|
+
}
|
|
997
1274
|
function findDocusaurusProjectRoot(repositoryDir, docsDir) {
|
|
998
1275
|
if (docsDir !== void 0) {
|
|
999
|
-
let candidate = docsDir
|
|
1276
|
+
let candidate = trimTrailingSlashes(docsDir);
|
|
1000
1277
|
while (true) {
|
|
1001
1278
|
const path = resolveWithin(repositoryDir, candidate || ".");
|
|
1002
1279
|
if (hasDocusaurusConfig(path)) return path;
|
|
@@ -1023,14 +1300,14 @@ function findDocusaurusProjectRoot(repositoryDir, docsDir) {
|
|
|
1023
1300
|
return null;
|
|
1024
1301
|
}
|
|
1025
1302
|
function readDocusaurusConfigSource(projectRoot) {
|
|
1026
|
-
const path = DOCUSAURUS_CONFIG_FILENAMES.map((filename) => resolveWithin(projectRoot, filename)).find((candidate) => existsSync3(candidate) &&
|
|
1027
|
-
if (!path ||
|
|
1303
|
+
const path = DOCUSAURUS_CONFIG_FILENAMES.map((filename) => resolveWithin(projectRoot, filename)).find((candidate) => existsSync3(candidate) && lstatSync3(candidate).isFile());
|
|
1304
|
+
if (!path || lstatSync3(path).size > 1e6) return "";
|
|
1028
1305
|
return readFileSync3(path, "utf8");
|
|
1029
1306
|
}
|
|
1030
1307
|
function primaryDocusaurusDocsDirectory(projectRoot) {
|
|
1031
1308
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
1032
1309
|
const configured = source.match(/\bdocs\s*:\s*\{[\s\S]{0,4000}?\bpath\s*:\s*(['"])([^'"]+)\1/)?.[2];
|
|
1033
|
-
return configured?.replace(/^\.\//, "")
|
|
1310
|
+
return trimTrailingSlashes(configured?.replace(/^\.\//, "") ?? "") || "docs";
|
|
1034
1311
|
}
|
|
1035
1312
|
function additionalDocusaurusPluginRoots(repositoryDir, projectRoot) {
|
|
1036
1313
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
@@ -1038,12 +1315,12 @@ function additionalDocusaurusPluginRoots(repositoryDir, projectRoot) {
|
|
|
1038
1315
|
const plugins = [];
|
|
1039
1316
|
const matcher = /['"]@docusaurus\/plugin-content-docs['"][\s\S]{0,3000}?\bpath\s*:\s*(['"])([^'"]+)\1[\s\S]{0,1000}?\brouteBasePath\s*:\s*(['"])([^'"]+)\3/g;
|
|
1040
1317
|
for (const match of source.matchAll(matcher)) {
|
|
1041
|
-
const localPath = match[2].replace(/^\.\//, "")
|
|
1042
|
-
const routePrefix = match[4]
|
|
1318
|
+
const localPath = trimTrailingSlashes(match[2].replace(/^\.\//, ""));
|
|
1319
|
+
const routePrefix = trimEdgeSlashes(match[4]);
|
|
1043
1320
|
if (!localPath || !routePrefix) continue;
|
|
1044
1321
|
const docsDir = [projectRelative, localPath].filter(Boolean).join("/");
|
|
1045
1322
|
const absolute = resolveWithin(repositoryDir, docsDir);
|
|
1046
|
-
if (!existsSync3(absolute) || !
|
|
1323
|
+
if (!existsSync3(absolute) || !lstatSync3(absolute).isDirectory()) continue;
|
|
1047
1324
|
plugins.push({ docsDir, routePrefix });
|
|
1048
1325
|
}
|
|
1049
1326
|
return plugins;
|
|
@@ -1100,13 +1377,13 @@ async function cloneGitHubRepository(source, targetDir) {
|
|
|
1100
1377
|
});
|
|
1101
1378
|
});
|
|
1102
1379
|
}
|
|
1103
|
-
function detectRepositoryPlatform(repositoryDir) {
|
|
1104
|
-
if (
|
|
1105
|
-
const
|
|
1380
|
+
function detectRepositoryPlatform(repositoryDir, docsDir) {
|
|
1381
|
+
if (findMintlifyProjectRoot(repositoryDir, docsDir)) return "mintlify";
|
|
1382
|
+
const selectedRoot = docsDir === void 0 ? repositoryDir : resolveWithin(repositoryDir, docsDir || ".");
|
|
1383
|
+
const docsJson = resolveWithin(selectedRoot, "docs.json");
|
|
1106
1384
|
if (existsSync3(docsJson)) {
|
|
1107
1385
|
try {
|
|
1108
1386
|
const config = JSON.parse(readFileSync3(docsJson, "utf8"));
|
|
1109
|
-
if (String(config.$schema ?? "").includes("mintlify") || "navigation" in config) return "mintlify";
|
|
1110
1387
|
if (Array.isArray(config.tabs)) return "thally";
|
|
1111
1388
|
} catch {
|
|
1112
1389
|
}
|
|
@@ -1132,7 +1409,7 @@ function containsMarkdown(directory, depth = 0) {
|
|
|
1132
1409
|
function detectRepositoryDocsDir(repositoryDir) {
|
|
1133
1410
|
for (const candidate of ["docs", "documentation", "content", "pages", "src/content", "src/pages", "guide", "guides", ""]) {
|
|
1134
1411
|
const path = resolveWithin(repositoryDir, candidate);
|
|
1135
|
-
if (existsSync3(path) &&
|
|
1412
|
+
if (existsSync3(path) && lstatSync3(path).isDirectory() && containsMarkdown(path)) return candidate;
|
|
1136
1413
|
}
|
|
1137
1414
|
return "";
|
|
1138
1415
|
}
|
|
@@ -1154,30 +1431,135 @@ function scanFiles(root) {
|
|
|
1154
1431
|
function normalizedReferenceKey(value) {
|
|
1155
1432
|
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "").replace(/\/(?:index|readme)$/i, "").replace(/^(?:index|readme)$/i, "") || "introduction";
|
|
1156
1433
|
}
|
|
1434
|
+
function exactReferenceKey(value) {
|
|
1435
|
+
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
1436
|
+
}
|
|
1157
1437
|
function findOpenApi(files) {
|
|
1158
1438
|
return files.find((file) => OPENAPI_FILENAMES.has(basename(file.relativePath).toLowerCase())) ?? null;
|
|
1159
1439
|
}
|
|
1440
|
+
function findConfiguredMintlifyOpenApi(config, files) {
|
|
1441
|
+
const api = config?.api && typeof config.api === "object" && !Array.isArray(config.api) ? config.api : null;
|
|
1442
|
+
const configured = typeof api?.openapi === "string" ? [api.openapi] : Array.isArray(api?.openapi) ? api.openapi.filter((value) => typeof value === "string") : [];
|
|
1443
|
+
for (const reference of configured) {
|
|
1444
|
+
if (/^(?:https?:)?\/\//i.test(reference)) continue;
|
|
1445
|
+
const key = reference.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/");
|
|
1446
|
+
const match = files.find((file) => file.relativePath === key);
|
|
1447
|
+
if (match) return match;
|
|
1448
|
+
}
|
|
1449
|
+
return null;
|
|
1450
|
+
}
|
|
1160
1451
|
function withoutFrontmatter(value) {
|
|
1161
1452
|
return value.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
1162
1453
|
}
|
|
1163
|
-
function
|
|
1454
|
+
function staticSnippetProperties(attributes) {
|
|
1455
|
+
const properties = /* @__PURE__ */ new Map();
|
|
1456
|
+
const matcher = /\b([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?:"([^"]*)"|'([^']*)'|\{\s*"([^"]*)"\s*\}|\{\s*'([^']*)'\s*\})/g;
|
|
1457
|
+
for (const match of attributes.matchAll(matcher)) {
|
|
1458
|
+
properties.set(match[1], match[2] ?? match[3] ?? match[4] ?? match[5] ?? "");
|
|
1459
|
+
}
|
|
1460
|
+
return properties;
|
|
1461
|
+
}
|
|
1462
|
+
function interpolateSnippet(snippet, attributes) {
|
|
1463
|
+
const properties = staticSnippetProperties(attributes);
|
|
1464
|
+
if (properties.size === 0) return snippet;
|
|
1465
|
+
let fence = "";
|
|
1466
|
+
return snippet.split("\n").map((line) => {
|
|
1467
|
+
const marker = line.match(/^\s*(`{3,}|~{3,})/)?.[1];
|
|
1468
|
+
if (marker) {
|
|
1469
|
+
if (!fence) fence = marker[0];
|
|
1470
|
+
else if (marker[0] === fence) fence = "";
|
|
1471
|
+
return line;
|
|
1472
|
+
}
|
|
1473
|
+
if (fence) return line;
|
|
1474
|
+
return line.replace(/\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (original, name) => {
|
|
1475
|
+
return properties.get(name) ?? original;
|
|
1476
|
+
});
|
|
1477
|
+
}).join("\n");
|
|
1478
|
+
}
|
|
1479
|
+
function resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot) {
|
|
1480
|
+
const repositoryRelative = sourcePath.startsWith("@site/") ? relative3(repositoryRoot, resolveWithin(siteRoot, sourcePath.slice("@site/".length))).replace(/\\/g, "/") : sourcePath.startsWith("/") ? relative3(repositoryRoot, resolveWithin(siteRoot, sourcePath.replace(/^\/+/, ""))).replace(/\\/g, "/") : relative3(repositoryRoot, resolvePath(dirname2(currentFile), sourcePath)).replace(/\\/g, "/");
|
|
1481
|
+
const candidate = resolveWithin(repositoryRoot, repositoryRelative);
|
|
1482
|
+
resolveWithin(repositoryRoot, relative3(repositoryRoot, candidate));
|
|
1483
|
+
return candidate;
|
|
1484
|
+
}
|
|
1485
|
+
function globalSnippetAliases(files, repositoryRoot, siteRoot) {
|
|
1486
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
1487
|
+
const matcher = /^import\s+(?:\{\s*default\s+as\s+)?([A-Z][A-Za-z0-9_]*)\s*\}?\s+from\s+['"]([^'"]+\.mdx?)['"]\s*;?(?:\s*\/\/.*)?$/gm;
|
|
1488
|
+
for (const file of files) {
|
|
1489
|
+
const segments = file.relativePath.split("/");
|
|
1490
|
+
if (!segments.some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase()))) continue;
|
|
1491
|
+
const basenameWithoutExtension = basename(file.relativePath).replace(/\.mdx?$/i, "");
|
|
1492
|
+
const conventionalAlias = basenameWithoutExtension.split(/[^A-Za-z0-9]+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
1493
|
+
if (conventionalAlias) aliases.set(conventionalAlias, file.absolutePath);
|
|
1494
|
+
}
|
|
1495
|
+
for (const file of files) {
|
|
1496
|
+
if (![".md", ".mdx"].includes(extname3(file.relativePath).toLowerCase())) continue;
|
|
1497
|
+
const raw = readFileSync3(file.absolutePath, "utf8");
|
|
1498
|
+
for (const match of raw.matchAll(matcher)) {
|
|
1499
|
+
try {
|
|
1500
|
+
const candidate = resolveSnippetPath(match[2], file.absolutePath, repositoryRoot, siteRoot);
|
|
1501
|
+
if (existsSync3(candidate) && lstatSync3(candidate).isFile()) {
|
|
1502
|
+
aliases.set(match[1], candidate);
|
|
1503
|
+
}
|
|
1504
|
+
} catch {
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
return aliases;
|
|
1509
|
+
}
|
|
1510
|
+
function repositoryAssetHref(value, currentFile, siteRoot) {
|
|
1511
|
+
const isBracketed = value.startsWith("<") && value.endsWith(">");
|
|
1512
|
+
const raw = isBracketed ? value.slice(1, -1) : value;
|
|
1513
|
+
if (!raw || raw.startsWith("#") || /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(raw)) return null;
|
|
1514
|
+
const suffixIndex = raw.search(/[?#]/);
|
|
1515
|
+
const pathname = suffixIndex >= 0 ? raw.slice(0, suffixIndex) : raw;
|
|
1516
|
+
const suffix = suffixIndex >= 0 ? raw.slice(suffixIndex) : "";
|
|
1517
|
+
let decodedPath;
|
|
1518
|
+
try {
|
|
1519
|
+
decodedPath = decodeURIComponent(pathname);
|
|
1520
|
+
} catch {
|
|
1521
|
+
return null;
|
|
1522
|
+
}
|
|
1523
|
+
if (!ASSET_EXTENSIONS.has(extname3(decodedPath).toLowerCase())) return null;
|
|
1524
|
+
try {
|
|
1525
|
+
const candidate = decodedPath.startsWith("/") ? resolveWithin(siteRoot, decodedPath.replace(/^\/+/, "")) : resolveWithin(dirname2(currentFile), decodedPath);
|
|
1526
|
+
const siteRelative = relative3(siteRoot, candidate).replace(/\\/g, "/");
|
|
1527
|
+
resolveWithin(siteRoot, siteRelative);
|
|
1528
|
+
if (!existsSync3(candidate) || !lstatSync3(candidate).isFile()) return null;
|
|
1529
|
+
const normalized = normalizeAssetPath(siteRelative);
|
|
1530
|
+
if (!normalized) return null;
|
|
1531
|
+
const rewritten = `/${normalized}${suffix}`;
|
|
1532
|
+
return isBracketed ? `<${rewritten}>` : rewritten;
|
|
1533
|
+
} catch {
|
|
1534
|
+
return null;
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
function rewriteRepositoryAssetLinks(body, currentFile, siteRoot) {
|
|
1538
|
+
return body.replace(/(!?\[[^\]]*\]\()(<[^>]+>|[^)\s]+)([^)]*\))/g, (original, opening, destination, closing) => {
|
|
1539
|
+
const rewritten = repositoryAssetHref(destination, currentFile, siteRoot);
|
|
1540
|
+
return rewritten ? `${opening}${rewritten}${closing}` : original;
|
|
1541
|
+
}).replace(/\b(src|img|image|href)=(['"])([^'"]+)\2/g, (original, property, quote, destination) => {
|
|
1542
|
+
const rewritten = repositoryAssetHref(destination, currentFile, siteRoot);
|
|
1543
|
+
return rewritten ? `${property}=${quote}${rewritten}${quote}` : original;
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0, siteRoot = repositoryRoot, globalAliases = /* @__PURE__ */ new Map()) {
|
|
1164
1547
|
if (depth >= 8) return raw;
|
|
1165
1548
|
const snippets = /* @__PURE__ */ new Map();
|
|
1166
1549
|
const withoutImports = raw.replace(
|
|
1167
1550
|
/^import\s+(?:\{\s*default\s+as\s+)?([A-Z][A-Za-z0-9_]*)\s*\}?\s+from\s+['"]([^'"]+\.mdx?)['"]\s*;?(?:\s*\/\/.*)?$/gm,
|
|
1168
1551
|
(_statement, componentName, sourcePath) => {
|
|
1169
1552
|
try {
|
|
1170
|
-
const
|
|
1171
|
-
|
|
1172
|
-
resolveWithin(repositoryRoot, relative3(repositoryRoot, candidate));
|
|
1173
|
-
if (!existsSync3(candidate) || !lstatSync2(candidate).isFile()) throw new Error("file not found");
|
|
1553
|
+
const candidate = resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot);
|
|
1554
|
+
if (!existsSync3(candidate) || !lstatSync3(candidate).isFile()) throw new Error("file not found");
|
|
1174
1555
|
const nested = inlineMdxSnippets(
|
|
1175
1556
|
withoutFrontmatter(readFileSync3(candidate, "utf8")),
|
|
1176
1557
|
candidate,
|
|
1177
1558
|
repositoryRoot,
|
|
1178
1559
|
warnings,
|
|
1179
1560
|
depth + 1,
|
|
1180
|
-
siteRoot
|
|
1561
|
+
siteRoot,
|
|
1562
|
+
globalAliases
|
|
1181
1563
|
);
|
|
1182
1564
|
snippets.set(componentName, nested);
|
|
1183
1565
|
return "";
|
|
@@ -1192,26 +1574,40 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
1192
1574
|
}
|
|
1193
1575
|
}
|
|
1194
1576
|
);
|
|
1577
|
+
for (const [componentName, candidate] of globalAliases) {
|
|
1578
|
+
if (snippets.has(componentName) || !new RegExp(`<${componentName}(?:\\s|/?>)`).test(withoutImports)) continue;
|
|
1579
|
+
const nested = inlineMdxSnippets(
|
|
1580
|
+
withoutFrontmatter(readFileSync3(candidate, "utf8")),
|
|
1581
|
+
candidate,
|
|
1582
|
+
repositoryRoot,
|
|
1583
|
+
warnings,
|
|
1584
|
+
depth + 1,
|
|
1585
|
+
siteRoot,
|
|
1586
|
+
globalAliases
|
|
1587
|
+
);
|
|
1588
|
+
snippets.set(componentName, nested);
|
|
1589
|
+
}
|
|
1195
1590
|
let result = withoutImports;
|
|
1196
1591
|
for (const [componentName, snippet] of snippets) {
|
|
1197
|
-
result = result.replace(new RegExp(`<${componentName}(?:\\s[^>]*)
|
|
1592
|
+
result = result.replace(new RegExp(`<${componentName}((?:\\s[^>]*)?)\\s*/>`, "g"), (_tag, attributes) => {
|
|
1593
|
+
return interpolateSnippet(snippet, attributes);
|
|
1594
|
+
}).replace(new RegExp(`<${componentName}((?:\\s[^>]*)?)>(?:[\\s\\S]*?)<\\/${componentName}>`, "g"), (_tag, attributes) => {
|
|
1595
|
+
return interpolateSnippet(snippet, attributes);
|
|
1596
|
+
});
|
|
1198
1597
|
}
|
|
1199
1598
|
return result;
|
|
1200
1599
|
}
|
|
1201
1600
|
function injectOpenApi(config, filename) {
|
|
1202
|
-
const tabs = config.tabs.
|
|
1203
|
-
const apiTab =
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
};
|
|
1207
|
-
const changelog = tabs.findIndex((tab) => tab.tab.toLowerCase() === "changelog");
|
|
1208
|
-
if (changelog >= 0) tabs.splice(changelog, 0, apiTab);
|
|
1209
|
-
else tabs.push(apiTab);
|
|
1601
|
+
const tabs = config.tabs.map((tab) => ({ ...tab }));
|
|
1602
|
+
const apiTab = tabs.find((tab) => tab.tab.toLowerCase().includes("api"));
|
|
1603
|
+
if (apiTab) apiTab.api = { source: `/${filename}`, navigation: false };
|
|
1604
|
+
else tabs.push({ tab: "API Reference", api: { source: `/${filename}` } });
|
|
1210
1605
|
return { ...config, tabs };
|
|
1211
1606
|
}
|
|
1212
1607
|
function migrateRepository(options) {
|
|
1213
1608
|
const repositoryDir = options.repositoryDir;
|
|
1214
|
-
const platform = options.platform ?? detectRepositoryPlatform(repositoryDir);
|
|
1609
|
+
const platform = options.platform ?? detectRepositoryPlatform(repositoryDir, options.docsDir);
|
|
1610
|
+
const mintlifyProjectRoot = platform === "mintlify" ? findMintlifyProjectRoot(repositoryDir, options.docsDir) : null;
|
|
1215
1611
|
const docusaurusProjectRoot = platform === "docusaurus" ? findDocusaurusProjectRoot(repositoryDir, options.docsDir) : null;
|
|
1216
1612
|
const configuredDocsDir = (() => {
|
|
1217
1613
|
if (options.docsDir !== void 0) {
|
|
@@ -1226,23 +1622,30 @@ function migrateRepository(options) {
|
|
|
1226
1622
|
const projectRelative = relative3(repositoryDir, docusaurusProjectRoot).replace(/\\/g, "/");
|
|
1227
1623
|
return [projectRelative, primaryDocusaurusDocsDirectory(docusaurusProjectRoot)].filter(Boolean).join("/");
|
|
1228
1624
|
}
|
|
1625
|
+
if (platform === "mintlify" && mintlifyProjectRoot) {
|
|
1626
|
+
return relative3(repositoryDir, mintlifyProjectRoot).replace(/\\/g, "/");
|
|
1627
|
+
}
|
|
1229
1628
|
return platform === "mintlify" ? "" : detectRepositoryDocsDir(repositoryDir);
|
|
1230
1629
|
})();
|
|
1231
1630
|
const warnings = [];
|
|
1232
1631
|
let docsConfig = { tabs: [] };
|
|
1233
1632
|
const referenceMap = /* @__PURE__ */ new Map();
|
|
1633
|
+
const exactReferenceMap = /* @__PURE__ */ new Map();
|
|
1234
1634
|
const referenceOrder = /* @__PURE__ */ new Map();
|
|
1235
1635
|
let docusaurusSidebars = null;
|
|
1636
|
+
let mintlifyConfig = null;
|
|
1236
1637
|
if (platform === "mintlify") {
|
|
1237
1638
|
try {
|
|
1238
|
-
const config = readMintlifyConfig(repositoryDir);
|
|
1639
|
+
const config = readMintlifyConfig(mintlifyProjectRoot ?? repositoryDir);
|
|
1239
1640
|
if (config) {
|
|
1641
|
+
mintlifyConfig = config;
|
|
1240
1642
|
const projected = projectMintlifyNavigation(config);
|
|
1241
1643
|
docsConfig = projected.docsConfig;
|
|
1242
1644
|
warnings.push(...projected.warnings);
|
|
1243
1645
|
for (const [index, reference] of projected.pageReferences.entries()) {
|
|
1244
1646
|
const key = normalizedReferenceKey(reference.ref);
|
|
1245
1647
|
referenceMap.set(key, reference);
|
|
1648
|
+
exactReferenceMap.set(exactReferenceKey(reference.ref), reference);
|
|
1246
1649
|
referenceOrder.set(key, index);
|
|
1247
1650
|
}
|
|
1248
1651
|
}
|
|
@@ -1264,7 +1667,7 @@ function migrateRepository(options) {
|
|
|
1264
1667
|
}
|
|
1265
1668
|
}
|
|
1266
1669
|
const contentRoot = resolveWithin(repositoryDir, configuredDocsDir);
|
|
1267
|
-
if (!existsSync3(contentRoot) || !
|
|
1670
|
+
if (!existsSync3(contentRoot) || !lstatSync3(contentRoot).isDirectory()) {
|
|
1268
1671
|
throw new Error(`Documentation directory does not exist: ${configuredDocsDir || "."}`);
|
|
1269
1672
|
}
|
|
1270
1673
|
const files = scanFiles(contentRoot);
|
|
@@ -1280,6 +1683,7 @@ function migrateRepository(options) {
|
|
|
1280
1683
|
const rightOrder = referenceOrder.get(normalizedReferenceKey(right.relativePath)) ?? Number.MAX_SAFE_INTEGER;
|
|
1281
1684
|
return leftOrder - rightOrder || left.relativePath.localeCompare(right.relativePath);
|
|
1282
1685
|
});
|
|
1686
|
+
const snippetAliases = platform === "mintlify" && mintlifyProjectRoot ? globalSnippetAliases(files, repositoryDir, mintlifyProjectRoot) : /* @__PURE__ */ new Map();
|
|
1283
1687
|
for (const file of pageFiles) {
|
|
1284
1688
|
if (!isDocumentationExtension(file.relativePath)) continue;
|
|
1285
1689
|
const sourceSegments = file.relativePath.split("/");
|
|
@@ -1288,7 +1692,7 @@ function migrateRepository(options) {
|
|
|
1288
1692
|
continue;
|
|
1289
1693
|
}
|
|
1290
1694
|
const rootFilename = !file.relativePath.includes("/") ? basename(file.relativePath).toLowerCase() : "";
|
|
1291
|
-
if (
|
|
1695
|
+
if (REPOSITORY_ONLY_DOCUMENTS.has(rootFilename) && !exactReferenceMap.has(exactReferenceKey(file.relativePath))) {
|
|
1292
1696
|
skipped++;
|
|
1293
1697
|
continue;
|
|
1294
1698
|
}
|
|
@@ -1297,21 +1701,21 @@ function migrateRepository(options) {
|
|
|
1297
1701
|
warnings.push({ code: "skipped-file", message: "Only Markdown and MDX are imported without an explicit converter.", source: file.relativePath });
|
|
1298
1702
|
continue;
|
|
1299
1703
|
}
|
|
1300
|
-
const size =
|
|
1704
|
+
const size = lstatSync3(file.absolutePath).size;
|
|
1301
1705
|
if (size > MAX_PAGE_BYTES) {
|
|
1302
1706
|
skipped++;
|
|
1303
1707
|
warnings.push({ code: "skipped-file", message: "Page exceeded the 2 MB repository import limit.", source: file.relativePath });
|
|
1304
1708
|
continue;
|
|
1305
1709
|
}
|
|
1306
1710
|
const key = normalizedReferenceKey(file.relativePath);
|
|
1307
|
-
const referenced = referenceMap.get(key);
|
|
1711
|
+
const referenced = exactReferenceMap.get(exactReferenceKey(file.relativePath)) ?? referenceMap.get(key);
|
|
1308
1712
|
let locale = referenced?.locale;
|
|
1309
|
-
let navigationId = referenced?.navigationId ?? pageIdFromReference(file.relativePath);
|
|
1713
|
+
let navigationId = referenced?.navigationId ?? pageIdFromReference(file.relativePath, platform === "mintlify");
|
|
1310
1714
|
if (!referenced && localeConfig) {
|
|
1311
1715
|
const prefix = file.relativePath.split("/", 1)[0];
|
|
1312
1716
|
if (localeConfig.locales.some((entry) => entry.code === prefix)) {
|
|
1313
1717
|
locale = prefix;
|
|
1314
|
-
navigationId = pageIdFromReference(file.relativePath.slice(prefix.length + 1));
|
|
1718
|
+
navigationId = pageIdFromReference(file.relativePath.slice(prefix.length + 1), platform === "mintlify");
|
|
1315
1719
|
}
|
|
1316
1720
|
}
|
|
1317
1721
|
if (!navigationId) {
|
|
@@ -1327,7 +1731,8 @@ function migrateRepository(options) {
|
|
|
1327
1731
|
repositoryDir,
|
|
1328
1732
|
warnings,
|
|
1329
1733
|
0,
|
|
1330
|
-
docusaurusProjectRoot ?? repositoryDir
|
|
1734
|
+
mintlifyProjectRoot ?? docusaurusProjectRoot ?? repositoryDir,
|
|
1735
|
+
snippetAliases
|
|
1331
1736
|
);
|
|
1332
1737
|
let docusaurusDescriptor;
|
|
1333
1738
|
const page = parseMarkdownPage({
|
|
@@ -1335,11 +1740,11 @@ function migrateRepository(options) {
|
|
|
1335
1740
|
navigationId,
|
|
1336
1741
|
...locale ? { locale } : {},
|
|
1337
1742
|
raw,
|
|
1338
|
-
source: `${options.sourceUrl}#${file.
|
|
1743
|
+
source: `${options.sourceUrl}#${relative3(repositoryDir, file.absolutePath).replace(/\\/g, "/")}`,
|
|
1339
1744
|
...platform === "docusaurus" ? {
|
|
1340
1745
|
resolveIdentity: (frontmatter, fallback) => {
|
|
1341
1746
|
const resolved = resolveDocusaurusPageIdentity(file.relativePath, frontmatter, fallback);
|
|
1342
|
-
const routePrefix = options.docusaurusRoutePrefix
|
|
1747
|
+
const routePrefix = options.docusaurusRoutePrefix === void 0 ? void 0 : trimEdgeSlashes(options.docusaurusRoutePrefix);
|
|
1343
1748
|
if (!routePrefix) {
|
|
1344
1749
|
docusaurusDescriptor = resolved.descriptor;
|
|
1345
1750
|
return resolved.identity;
|
|
@@ -1362,6 +1767,9 @@ function migrateRepository(options) {
|
|
|
1362
1767
|
skipped++;
|
|
1363
1768
|
continue;
|
|
1364
1769
|
}
|
|
1770
|
+
if (platform === "mintlify" && mintlifyProjectRoot) {
|
|
1771
|
+
page.body = rewriteRepositoryAssetLinks(page.body, file.absolutePath, mintlifyProjectRoot);
|
|
1772
|
+
}
|
|
1365
1773
|
if (seenPageIds.has(page.id)) {
|
|
1366
1774
|
skipped++;
|
|
1367
1775
|
warnings.push({ code: "collision", message: `Multiple source files map to ${page.id}; the first file was kept.`, source: file.relativePath });
|
|
@@ -1382,7 +1790,7 @@ function migrateRepository(options) {
|
|
|
1382
1790
|
const docusaurusAssetRoot = docusaurusProjectRoot ?? repositoryDir;
|
|
1383
1791
|
const repositoryAssets = platform === "docusaurus" && configuredDocsDir && !options.docusaurusSkipAssets ? ["static", "public"].flatMap((directory) => {
|
|
1384
1792
|
const root = resolveWithin(docusaurusAssetRoot, directory);
|
|
1385
|
-
if (!existsSync3(root) || !
|
|
1793
|
+
if (!existsSync3(root) || !lstatSync3(root).isDirectory()) return [];
|
|
1386
1794
|
return scanFiles(root).map((file) => ({
|
|
1387
1795
|
...file,
|
|
1388
1796
|
relativePath: `${directory}/${file.relativePath}`
|
|
@@ -1391,8 +1799,9 @@ function migrateRepository(options) {
|
|
|
1391
1799
|
let totalAssetBytes = 0;
|
|
1392
1800
|
for (const file of [...files, ...repositoryAssets]) {
|
|
1393
1801
|
const firstSegment = file.relativePath.split("/", 1)[0].toLowerCase();
|
|
1394
|
-
if (!
|
|
1395
|
-
|
|
1802
|
+
if (!ASSET_EXTENSIONS.has(extname3(file.relativePath).toLowerCase())) continue;
|
|
1803
|
+
if (platform !== "mintlify" && !ASSET_DIRECTORIES.has(firstSegment)) continue;
|
|
1804
|
+
const size = lstatSync3(file.absolutePath).size;
|
|
1396
1805
|
if (size > MAX_ASSET_BYTES || totalAssetBytes + size > MAX_TOTAL_ASSET_BYTES) {
|
|
1397
1806
|
warnings.push({ code: "limit-reached", message: "An asset was skipped because the migration asset budget was exhausted.", source: file.relativePath });
|
|
1398
1807
|
continue;
|
|
@@ -1430,7 +1839,7 @@ function migrateRepository(options) {
|
|
|
1430
1839
|
}
|
|
1431
1840
|
}
|
|
1432
1841
|
if (docsConfig.tabs.length === 0) docsConfig = buildNavigationFromPages(pages);
|
|
1433
|
-
const openApi = findOpenApi(files);
|
|
1842
|
+
const openApi = findConfiguredMintlifyOpenApi(mintlifyConfig, files) ?? findOpenApi(files);
|
|
1434
1843
|
if (openApi) {
|
|
1435
1844
|
const filename = basename(openApi.relativePath);
|
|
1436
1845
|
if (!assets.some((asset) => asset.path === filename)) {
|
|
@@ -1438,6 +1847,7 @@ function migrateRepository(options) {
|
|
|
1438
1847
|
}
|
|
1439
1848
|
docsConfig = injectOpenApi(docsConfig, filename);
|
|
1440
1849
|
}
|
|
1850
|
+
if (platform === "mintlify") docsConfig = addMintlifyDirectoryRedirects(docsConfig, pages);
|
|
1441
1851
|
if (platform === "docusaurus" && docusaurusProjectRoot && !options.docusaurusSkipPlugins) {
|
|
1442
1852
|
for (const plugin of additionalDocusaurusPluginRoots(repositoryDir, docusaurusProjectRoot)) {
|
|
1443
1853
|
if (plugin.docsDir === configuredDocsDir) continue;
|
|
@@ -1479,6 +1889,12 @@ function migrateRepository(options) {
|
|
|
1479
1889
|
pages,
|
|
1480
1890
|
assets,
|
|
1481
1891
|
docsConfig,
|
|
1892
|
+
...mintlifyConfig ? {
|
|
1893
|
+
site: {
|
|
1894
|
+
...typeof mintlifyConfig.name === "string" ? { name: mintlifyConfig.name } : {},
|
|
1895
|
+
...typeof mintlifyConfig.description === "string" ? { description: mintlifyConfig.description } : {}
|
|
1896
|
+
}
|
|
1897
|
+
} : {},
|
|
1482
1898
|
warnings,
|
|
1483
1899
|
stats: { discovered, imported: pages.length, skipped }
|
|
1484
1900
|
};
|
|
@@ -1492,8 +1908,13 @@ function renderPage(bundle, page) {
|
|
|
1492
1908
|
return [
|
|
1493
1909
|
"---",
|
|
1494
1910
|
`title: ${yamlString(page.title)}`,
|
|
1911
|
+
page.navTitle ? `navTitle: ${yamlString(page.navTitle)}` : null,
|
|
1495
1912
|
`description: ${yamlString(page.description)}`,
|
|
1913
|
+
page.badge ? `badge: ${yamlString(page.badge)}` : null,
|
|
1496
1914
|
page.keywords.length > 0 ? `keywords: [${page.keywords.map(yamlString).join(", ")}]` : null,
|
|
1915
|
+
page.mode ? `mode: ${yamlString(page.mode)}` : null,
|
|
1916
|
+
page.hidden ? "hidden: true" : null,
|
|
1917
|
+
page.noindex ? "noindex: true" : null,
|
|
1497
1918
|
page.openapi ? `openapi: ${yamlString(page.openapi)}` : null,
|
|
1498
1919
|
bundle.sourceKind === "url" ? `source: ${yamlString(page.source)}` : null,
|
|
1499
1920
|
"---",
|
|
@@ -1591,10 +2012,13 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
1591
2012
|
"Color",
|
|
1592
2013
|
"Column",
|
|
1593
2014
|
"Columns",
|
|
2015
|
+
"ContentPanel",
|
|
1594
2016
|
"Danger",
|
|
1595
2017
|
"Error",
|
|
1596
2018
|
"Expandable",
|
|
1597
2019
|
"Agent",
|
|
2020
|
+
"AgentPrompt",
|
|
2021
|
+
"BannerPreview",
|
|
1598
2022
|
"Embed",
|
|
1599
2023
|
"File",
|
|
1600
2024
|
"Folder",
|
|
@@ -1785,7 +2209,7 @@ function pageIdForUrl(url, scopePath) {
|
|
|
1785
2209
|
else if (path === scopePath) path = "";
|
|
1786
2210
|
else if (path.startsWith(`${scopePath}/`)) path = path.slice(scopePath.length + 1);
|
|
1787
2211
|
}
|
|
1788
|
-
return pageIdFromReference(path.replace(/^\/+/, "") || "introduction");
|
|
2212
|
+
return pageIdFromReference(path.replace(/^\/+/, "") || "introduction", true);
|
|
1789
2213
|
}
|
|
1790
2214
|
function canonicalLocalizedPageId(id) {
|
|
1791
2215
|
const [segment, ...rest] = id.split("/");
|
|
@@ -1807,6 +2231,72 @@ function detectUrlPlatform(document) {
|
|
|
1807
2231
|
if (value.includes("starlight")) return "starlight";
|
|
1808
2232
|
return "generic";
|
|
1809
2233
|
}
|
|
2234
|
+
function jsonObjectAfterKey(value, key) {
|
|
2235
|
+
let cursor = 0;
|
|
2236
|
+
const marker = `"${key}":`;
|
|
2237
|
+
while (cursor < value.length) {
|
|
2238
|
+
const markerIndex = value.indexOf(marker, cursor);
|
|
2239
|
+
if (markerIndex < 0) return null;
|
|
2240
|
+
let start = markerIndex + marker.length;
|
|
2241
|
+
while (/\s/.test(value[start] ?? "")) start += 1;
|
|
2242
|
+
if (value[start] !== "{") {
|
|
2243
|
+
cursor = start + 1;
|
|
2244
|
+
continue;
|
|
2245
|
+
}
|
|
2246
|
+
let depth = 0;
|
|
2247
|
+
let isQuoted = false;
|
|
2248
|
+
let isEscaped = false;
|
|
2249
|
+
for (let index = start; index < value.length; index += 1) {
|
|
2250
|
+
const character = value[index];
|
|
2251
|
+
if (isQuoted) {
|
|
2252
|
+
if (isEscaped) isEscaped = false;
|
|
2253
|
+
else if (character === "\\") isEscaped = true;
|
|
2254
|
+
else if (character === '"') isQuoted = false;
|
|
2255
|
+
continue;
|
|
2256
|
+
}
|
|
2257
|
+
if (character === '"') {
|
|
2258
|
+
isQuoted = true;
|
|
2259
|
+
continue;
|
|
2260
|
+
}
|
|
2261
|
+
if (character === "{") depth += 1;
|
|
2262
|
+
if (character === "}") {
|
|
2263
|
+
depth -= 1;
|
|
2264
|
+
if (depth !== 0) continue;
|
|
2265
|
+
try {
|
|
2266
|
+
const parsed = JSON.parse(value.slice(start, index + 1));
|
|
2267
|
+
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
|
|
2268
|
+
} catch {
|
|
2269
|
+
break;
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
cursor = start + 1;
|
|
2274
|
+
}
|
|
2275
|
+
return null;
|
|
2276
|
+
}
|
|
2277
|
+
function embeddedMintlifyConfig(body) {
|
|
2278
|
+
const $ = load(body);
|
|
2279
|
+
const flightPayloads = [];
|
|
2280
|
+
$("script").each((_index, element) => {
|
|
2281
|
+
const script = $(element).text();
|
|
2282
|
+
for (const match of script.matchAll(/self\.__next_f\.push\(\[1,("(?:\\.|[^"\\])*")\]\)/g)) {
|
|
2283
|
+
try {
|
|
2284
|
+
const payload2 = JSON.parse(match[1]);
|
|
2285
|
+
if (typeof payload2 === "string") flightPayloads.push(payload2);
|
|
2286
|
+
} catch {
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
});
|
|
2290
|
+
if (flightPayloads.length === 0) return null;
|
|
2291
|
+
const payload = flightPayloads.join("\n");
|
|
2292
|
+
const docsConfig = jsonObjectAfterKey(payload, "docsConfig");
|
|
2293
|
+
const scopedNavigation = jsonObjectAfterKey(payload, "scopedNav");
|
|
2294
|
+
if (!docsConfig && !scopedNavigation) return null;
|
|
2295
|
+
return {
|
|
2296
|
+
...docsConfig ?? {},
|
|
2297
|
+
...scopedNavigation ? { navigation: scopedNavigation } : {}
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
1810
2300
|
function plainDescription(value) {
|
|
1811
2301
|
return value.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[`*_>#-]/g, "").replace(/\s+/g, " ").trim().slice(0, 240);
|
|
1812
2302
|
}
|
|
@@ -2045,7 +2535,12 @@ function remoteMarkdownMetadata(body) {
|
|
|
2045
2535
|
return {
|
|
2046
2536
|
content: body.slice(contentStart),
|
|
2047
2537
|
title: scalar("title"),
|
|
2048
|
-
description: scalar("description")
|
|
2538
|
+
description: scalar("description"),
|
|
2539
|
+
sidebarTitle: scalar("sidebarTitle"),
|
|
2540
|
+
tag: scalar("tag"),
|
|
2541
|
+
mode: scalar("mode"),
|
|
2542
|
+
hidden: scalar("hidden") === "true" ? true : void 0,
|
|
2543
|
+
noindex: scalar("noindex") === "true" ? true : void 0
|
|
2049
2544
|
};
|
|
2050
2545
|
}
|
|
2051
2546
|
function stripMintlifyDocumentationIndex(body) {
|
|
@@ -2105,6 +2600,11 @@ function markdownPage(document, id) {
|
|
|
2105
2600
|
"---",
|
|
2106
2601
|
`title: ${JSON.stringify(title)}`,
|
|
2107
2602
|
...description ? [`description: ${JSON.stringify(description)}`] : [],
|
|
2603
|
+
...parsed.sidebarTitle ? [`sidebarTitle: ${JSON.stringify(parsed.sidebarTitle)}`] : [],
|
|
2604
|
+
...parsed.tag ? [`tag: ${JSON.stringify(parsed.tag)}`] : [],
|
|
2605
|
+
...parsed.mode ? [`mode: ${JSON.stringify(parsed.mode)}`] : [],
|
|
2606
|
+
...parsed.hidden ? ["hidden: true"] : [],
|
|
2607
|
+
...parsed.noindex ? ["noindex: true"] : [],
|
|
2108
2608
|
...embeddedOpenApi.openapi ? [`openapi: ${JSON.stringify(embeddedOpenApi.openapi)}`] : [],
|
|
2109
2609
|
"---",
|
|
2110
2610
|
"",
|
|
@@ -2487,7 +2987,7 @@ function htmlTopLevelNavigation(document, source, scopePath) {
|
|
|
2487
2987
|
const collectCandidates = (selector) => {
|
|
2488
2988
|
const candidates2 = [];
|
|
2489
2989
|
$(selector).each((_containerIndex, container) => {
|
|
2490
|
-
const
|
|
2990
|
+
const entries2 = [];
|
|
2491
2991
|
const seenSections = /* @__PURE__ */ new Set();
|
|
2492
2992
|
let anchorCount = 0;
|
|
2493
2993
|
$(container).find("a[href]").each((_index, element) => {
|
|
@@ -2503,22 +3003,40 @@ function htmlTopLevelNavigation(document, source, scopePath) {
|
|
|
2503
3003
|
const section = targetId.split("/", 1)[0] || "introduction";
|
|
2504
3004
|
if (!pageId || seenSections.has(section)) return;
|
|
2505
3005
|
seenSections.add(section);
|
|
2506
|
-
|
|
3006
|
+
entries2.push({ section, label, pageId });
|
|
2507
3007
|
} catch {
|
|
2508
3008
|
}
|
|
2509
3009
|
});
|
|
2510
|
-
if (
|
|
2511
|
-
candidates2.push({ entries, anchorCount });
|
|
3010
|
+
if (entries2.length > 1) {
|
|
3011
|
+
candidates2.push({ entries: entries2, anchorCount });
|
|
2512
3012
|
}
|
|
2513
3013
|
});
|
|
2514
3014
|
return candidates2;
|
|
2515
3015
|
};
|
|
2516
3016
|
const tabCandidates = collectCandidates(".nav-tabs");
|
|
2517
3017
|
const candidates = tabCandidates.length > 0 ? tabCandidates : collectCandidates('nav, [role="navigation"]');
|
|
2518
|
-
|
|
3018
|
+
const entries = candidates.sort((left, right) => {
|
|
2519
3019
|
const densityDifference = right.entries.length / right.anchorCount - left.entries.length / left.anchorCount;
|
|
2520
3020
|
return densityDifference || right.entries.length - left.entries.length;
|
|
2521
3021
|
})[0]?.entries ?? [];
|
|
3022
|
+
const normalizedLabels = entries.map((entry) => entry.label.trim().toLowerCase());
|
|
3023
|
+
if (entries.length > 12 || new Set(normalizedLabels).size !== normalizedLabels.length) return [];
|
|
3024
|
+
return entries;
|
|
3025
|
+
}
|
|
3026
|
+
function remapNavigationHome(config, sourceHomePageId) {
|
|
3027
|
+
if (!sourceHomePageId || sourceHomePageId === "introduction") return config;
|
|
3028
|
+
const remapGroups = (groups) => groups.map((group) => ({
|
|
3029
|
+
...group,
|
|
3030
|
+
pages: group.pages.map((page) => typeof page === "string" ? page === sourceHomePageId ? "introduction" : page : remapGroups([page])[0])
|
|
3031
|
+
}));
|
|
3032
|
+
return {
|
|
3033
|
+
...config,
|
|
3034
|
+
tabs: config.tabs.map((tab) => ({
|
|
3035
|
+
...tab,
|
|
3036
|
+
...tab.href === `/${sourceHomePageId}` ? { href: "/" } : {},
|
|
3037
|
+
...tab.groups ? { groups: remapGroups(tab.groups) } : {}
|
|
3038
|
+
}))
|
|
3039
|
+
};
|
|
2522
3040
|
}
|
|
2523
3041
|
function markdownLinks(body) {
|
|
2524
3042
|
return [...body.matchAll(/\[[^\]]+\]\(([^)\s]+)(?:\s+['"][^'"]*['"])?\)/g)].map((match) => match[1]);
|
|
@@ -2583,8 +3101,13 @@ async function migrateUrl(options) {
|
|
|
2583
3101
|
}
|
|
2584
3102
|
const docusaurusDiscovery = platform === "docusaurus" && docusaurusHtmlProbe ? deriveDocusaurusScope(source, docusaurusHtmlProbe, submittedScopePath) : null;
|
|
2585
3103
|
const scopePath = platform === "mintlify" ? machineIndexScopePath(source, [initial, scopedHtmlProbe]) ?? submittedScopePath : docusaurusDiscovery?.scopePath ?? submittedScopePath;
|
|
2586
|
-
const
|
|
2587
|
-
const
|
|
3104
|
+
const sourceMintlifyConfig = platform === "mintlify" && scopedHtmlProbe && /html/i.test(scopedHtmlProbe.contentType) ? embeddedMintlifyConfig(scopedHtmlProbe.body) : null;
|
|
3105
|
+
const sourceMintlifyProjection = sourceMintlifyConfig ? projectMintlifyNavigation(sourceMintlifyConfig, { pathPrefix: scopePath }) : null;
|
|
3106
|
+
const hasStructuredMintlifyNavigation = Boolean(
|
|
3107
|
+
sourceMintlifyProjection?.docsConfig.tabs.length && sourceMintlifyProjection.pageReferences.length
|
|
3108
|
+
);
|
|
3109
|
+
const sourceTopLevelNavigation = platform === "mintlify" && !hasStructuredMintlifyNavigation && scopedHtmlProbe && /html/i.test(scopedHtmlProbe.contentType) ? htmlTopLevelNavigation(scopedHtmlProbe, source, scopePath) : void 0;
|
|
3110
|
+
const sourceHomePageId = sourceMintlifyProjection?.pageReferences[0]?.navigationId ?? sourceTopLevelNavigation?.[0]?.pageId ?? (platform === "mintlify" && scopePath === "/" ? canonicalLocalizedPageId(pageIdForUrl(source, scopePath) ?? "") || void 0 : void 0);
|
|
2588
3111
|
const cache = /* @__PURE__ */ new Map([[source.toString(), initial]]);
|
|
2589
3112
|
if (docusaurusHtmlProbe) cache.set(docusaurusHtmlProbe.finalUrl.toString(), docusaurusHtmlProbe);
|
|
2590
3113
|
const queue = [source];
|
|
@@ -2605,6 +3128,13 @@ async function migrateUrl(options) {
|
|
|
2605
3128
|
queued.add(candidateIdentity(url));
|
|
2606
3129
|
queue.push(url);
|
|
2607
3130
|
}
|
|
3131
|
+
for (const reference of sourceMintlifyProjection?.pageReferences ?? []) {
|
|
3132
|
+
let href = reference.ref;
|
|
3133
|
+
if (scopePath !== "/" && href.startsWith("/") && href !== scopePath && !href.startsWith(`${scopePath}/`)) {
|
|
3134
|
+
href = `${scopePath}${href}`;
|
|
3135
|
+
}
|
|
3136
|
+
enqueue(href, source);
|
|
3137
|
+
}
|
|
2608
3138
|
const navigationProbe = docusaurusHtmlProbe ?? scopedHtmlProbe;
|
|
2609
3139
|
if (navigationProbe && /html/i.test(navigationProbe.contentType)) {
|
|
2610
3140
|
for (const link of htmlNavigationLinks(navigationProbe)) enqueue(link, navigationProbe.finalUrl);
|
|
@@ -2808,10 +3338,11 @@ async function migrateUrl(options) {
|
|
|
2808
3338
|
...entry,
|
|
2809
3339
|
pageId: entry.pageId === sourceHomePageId ? "introduction" : entry.pageId
|
|
2810
3340
|
}));
|
|
2811
|
-
|
|
3341
|
+
let docsConfig = platform === "docusaurus" ? docusaurusNavigationFromSnapshots(docusaurusSidebars, pages, scopePath) : hasStructuredMintlifyNavigation && sourceMintlifyProjection ? remapNavigationHome(sourceMintlifyProjection.docsConfig, sourceHomePageId) : buildNavigationFromPages(pages, {
|
|
2812
3342
|
topLevelTabs: platform === "mintlify",
|
|
2813
3343
|
topLevelNavigation
|
|
2814
3344
|
});
|
|
3345
|
+
if (sourceMintlifyProjection) warnings.push(...sourceMintlifyProjection.warnings);
|
|
2815
3346
|
if (docusaurusRedirects.length > 0) {
|
|
2816
3347
|
docsConfig.redirects = [...new Map(docusaurusRedirects.map((redirect) => [
|
|
2817
3348
|
`${redirect.source}:${redirect.destination}`,
|
|
@@ -2841,6 +3372,7 @@ async function migrateUrl(options) {
|
|
|
2841
3372
|
]
|
|
2842
3373
|
};
|
|
2843
3374
|
}
|
|
3375
|
+
if (platform === "mintlify") docsConfig = addMintlifyDirectoryRedirects(docsConfig, pages);
|
|
2844
3376
|
return {
|
|
2845
3377
|
sourceUrl: source.toString(),
|
|
2846
3378
|
sourceKind: "url",
|
|
@@ -2848,6 +3380,12 @@ async function migrateUrl(options) {
|
|
|
2848
3380
|
pages,
|
|
2849
3381
|
assets: openApiAsset ? [{ path: "openapi.yaml", content: openApiAsset }] : [],
|
|
2850
3382
|
docsConfig,
|
|
3383
|
+
...sourceMintlifyConfig ? {
|
|
3384
|
+
site: {
|
|
3385
|
+
...typeof sourceMintlifyConfig.name === "string" ? { name: sourceMintlifyConfig.name } : {},
|
|
3386
|
+
...typeof sourceMintlifyConfig.description === "string" ? { description: sourceMintlifyConfig.description } : {}
|
|
3387
|
+
}
|
|
3388
|
+
} : {},
|
|
2851
3389
|
warnings,
|
|
2852
3390
|
stats: {
|
|
2853
3391
|
discovered: visited.size,
|
|
@@ -2857,6 +3395,7 @@ async function migrateUrl(options) {
|
|
|
2857
3395
|
};
|
|
2858
3396
|
}
|
|
2859
3397
|
export {
|
|
3398
|
+
addMintlifyDirectoryRedirects,
|
|
2860
3399
|
buildNavigationFromPages,
|
|
2861
3400
|
cloneGitHubRepository,
|
|
2862
3401
|
defaultMigrationFetcher,
|