@thallylabs/migrate 0.2.0 → 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 +650 -87
- 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) {
|
|
@@ -520,8 +533,19 @@ function projectDocusaurusNavigation(input) {
|
|
|
520
533
|
};
|
|
521
534
|
}
|
|
522
535
|
|
|
523
|
-
// src/
|
|
536
|
+
// src/frontmatter.ts
|
|
524
537
|
import matter from "gray-matter";
|
|
538
|
+
var FRONTMATTER_OPTIONS = {
|
|
539
|
+
engines: {
|
|
540
|
+
javascript: () => ({}),
|
|
541
|
+
js: () => ({})
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
function parseFrontmatter(raw) {
|
|
545
|
+
return matter(raw, FRONTMATTER_OPTIONS);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/mdx.ts
|
|
525
549
|
function titleFromId(id) {
|
|
526
550
|
const value = id.split("/").at(-1) ?? id;
|
|
527
551
|
if (value === "introduction") return "Introduction";
|
|
@@ -574,10 +598,10 @@ function normalizeMdx(body) {
|
|
|
574
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) => {
|
|
575
599
|
const title = attributes.match(/\blabel=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? attributes.match(/\bvalue=(?:"([^"]*)"|'([^']*)')/)?.slice(1).find(Boolean) ?? "Tab";
|
|
576
600
|
return `<Tab title="${title.replace(/"/g, """)}">`;
|
|
577
|
-
}).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>");
|
|
578
602
|
}
|
|
579
603
|
function parseMarkdownPage(input) {
|
|
580
|
-
const parsed =
|
|
604
|
+
const parsed = parseFrontmatter(input.raw);
|
|
581
605
|
const fallbackIdentity = {
|
|
582
606
|
id: input.id,
|
|
583
607
|
navigationId: input.navigationId ?? input.id,
|
|
@@ -587,14 +611,23 @@ function parseMarkdownPage(input) {
|
|
|
587
611
|
const body = normalizeMdx(parsed.content).trim();
|
|
588
612
|
const keywords = Array.isArray(parsed.data.keywords) ? parsed.data.keywords.filter((value) => typeof value === "string") : [];
|
|
589
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;
|
|
590
618
|
const description = typeof parsed.data.description === "string" && parsed.data.description.trim() ? parsed.data.description.trim() : firstParagraph(body);
|
|
591
619
|
return {
|
|
592
620
|
id: identity.id,
|
|
593
621
|
navigationId: identity.navigationId,
|
|
594
622
|
locale: identity.locale,
|
|
595
623
|
title,
|
|
624
|
+
navTitle,
|
|
596
625
|
description,
|
|
626
|
+
badge,
|
|
597
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,
|
|
598
631
|
openapi: typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0,
|
|
599
632
|
body,
|
|
600
633
|
source: input.source
|
|
@@ -602,8 +635,24 @@ function parseMarkdownPage(input) {
|
|
|
602
635
|
}
|
|
603
636
|
|
|
604
637
|
// src/navigation.ts
|
|
605
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
638
|
+
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
606
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
|
+
}
|
|
607
656
|
var LANGUAGE_LABELS = {
|
|
608
657
|
ar: "Arabic",
|
|
609
658
|
de: "German",
|
|
@@ -625,6 +674,15 @@ var LANGUAGE_LABELS = {
|
|
|
625
674
|
function objectValue2(value) {
|
|
626
675
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
627
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
|
+
}
|
|
628
686
|
function labelFor(value, fallback) {
|
|
629
687
|
for (const key of ["tab", "group", "anchor", "product", "dropdown", "version", "menu", "label", "name", "title"]) {
|
|
630
688
|
if (typeof value[key] === "string" && value[key]) return String(value[key]);
|
|
@@ -638,11 +696,27 @@ function jsonPointer(root, pointer) {
|
|
|
638
696
|
if (!pointer || pointer === "#") return root;
|
|
639
697
|
const tokens = pointer.replace(/^#\/?/, "").split("/").filter(Boolean);
|
|
640
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
|
+
}
|
|
641
703
|
const object = objectValue2(current);
|
|
642
704
|
if (!object) return void 0;
|
|
643
705
|
return object[token.replace(/~1/g, "/").replace(/~0/g, "~")];
|
|
644
706
|
}, root);
|
|
645
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
|
+
}
|
|
646
720
|
function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
647
721
|
if (Array.isArray(value)) {
|
|
648
722
|
return value.map((entry) => resolveJsonReferences(entry, currentFile, repositoryRoot, stack));
|
|
@@ -657,11 +731,17 @@ function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
|
657
731
|
const stackKey = `${referencedFile}#${pointer}`;
|
|
658
732
|
if (stack.has(stackKey)) throw new Error(`Circular Mintlify $ref: ${object.$ref}`);
|
|
659
733
|
stack.add(stackKey);
|
|
660
|
-
const referencedRoot =
|
|
734
|
+
const referencedRoot = readMintlifyJson(referencedFile, repositoryRoot);
|
|
661
735
|
const referenced = jsonPointer(referencedRoot, pointer ? `#${pointer}` : "");
|
|
662
736
|
const resolved = resolveJsonReferences(referenced, referencedFile, repositoryRoot, stack);
|
|
663
737
|
stack.delete(stackKey);
|
|
664
|
-
|
|
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;
|
|
665
745
|
}
|
|
666
746
|
return Object.fromEntries(
|
|
667
747
|
Object.entries(object).map(([key, entry]) => [
|
|
@@ -673,18 +753,33 @@ function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
|
673
753
|
function readMintlifyConfig(repositoryRoot) {
|
|
674
754
|
const configPath = ["docs.json", "mint.json"].map((filename) => resolveWithin(repositoryRoot, filename)).find(existsSync2);
|
|
675
755
|
if (!configPath) return null;
|
|
676
|
-
const raw =
|
|
756
|
+
const raw = readMintlifyJson(configPath, repositoryRoot);
|
|
677
757
|
return resolveJsonReferences(raw, configPath, repositoryRoot, /* @__PURE__ */ new Set());
|
|
678
758
|
}
|
|
679
|
-
function normalizePageRef(value) {
|
|
759
|
+
function normalizePageRef(value, pathPrefix = "") {
|
|
680
760
|
if (/^(?:https?:)?\/\//i.test(value) || value.startsWith("#")) return null;
|
|
681
|
-
|
|
682
|
-
|
|
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;
|
|
683
778
|
}
|
|
684
779
|
function registerReference(value, context) {
|
|
685
780
|
const localePrefix = context.locale ? `${context.locale}/` : "";
|
|
686
781
|
const localizedValue = localePrefix && value.replace(/^\/+/, "").startsWith(localePrefix) ? value.replace(/^\/+/, "").slice(localePrefix.length) : value;
|
|
687
|
-
const navigationId = normalizePageRef(localizedValue);
|
|
782
|
+
const navigationId = normalizePageRef(localizedValue, context.pathPrefix);
|
|
688
783
|
if (!navigationId) return null;
|
|
689
784
|
const key = `${context.locale ?? ""}:${value}`;
|
|
690
785
|
if (!context.seenReferences.has(key)) {
|
|
@@ -698,6 +793,17 @@ function convertPage(value, context) {
|
|
|
698
793
|
const object = objectValue2(value);
|
|
699
794
|
if (!object) return null;
|
|
700
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
|
+
}
|
|
701
807
|
const pages = Array.isArray(object.pages) ? object.pages : [];
|
|
702
808
|
if ("group" in object || pages.length > 0) {
|
|
703
809
|
const children = [];
|
|
@@ -712,7 +818,8 @@ function convertPage(value, context) {
|
|
|
712
818
|
if (children.length === 0) return null;
|
|
713
819
|
return {
|
|
714
820
|
group: labelFor(object, "Documentation"),
|
|
715
|
-
...
|
|
821
|
+
...iconName(object.icon) ? { icon: iconName(object.icon) } : {},
|
|
822
|
+
...object.hidden === true ? { hidden: true } : {},
|
|
716
823
|
pages: children
|
|
717
824
|
};
|
|
718
825
|
}
|
|
@@ -739,12 +846,20 @@ function convertContainerToTabs(containerValue, context, fallbackTab) {
|
|
|
739
846
|
const object = objectValue2(value);
|
|
740
847
|
if (!object) return [];
|
|
741
848
|
const tab = labelFor(object, `${fallbackTab} ${index + 1}`);
|
|
742
|
-
|
|
743
|
-
|
|
849
|
+
const href = projectedHref(object.href);
|
|
850
|
+
if (href && !object.pages && !object.groups) {
|
|
851
|
+
return [{ tab, href, ...object.hidden === true ? { hidden: true } : {} }];
|
|
744
852
|
}
|
|
745
853
|
const nested = convertContainerToTabs(object, context, tab);
|
|
746
854
|
if (nested.length > 0) {
|
|
747
|
-
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
|
+
}
|
|
748
863
|
return nested.map((item) => ({ ...item, tab: `${tab}: ${item.tab}` }));
|
|
749
864
|
}
|
|
750
865
|
return [];
|
|
@@ -757,38 +872,168 @@ function convertContainerToTabs(containerValue, context, fallbackTab) {
|
|
|
757
872
|
];
|
|
758
873
|
if (typeof container.root === "string") rawPages.unshift(container.root);
|
|
759
874
|
const groups = convertGroups(rawPages, context, "Overview");
|
|
760
|
-
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;
|
|
761
906
|
}
|
|
762
|
-
function
|
|
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
|
+
};
|
|
996
|
+
}
|
|
997
|
+
function projectMintlifyNavigation(config, options = {}) {
|
|
763
998
|
const warnings = [];
|
|
764
999
|
const references = [];
|
|
765
1000
|
const seenReferences = /* @__PURE__ */ new Set();
|
|
1001
|
+
const warningKeys = /* @__PURE__ */ new Set();
|
|
766
1002
|
const navigation = objectValue2(config.navigation) ?? config;
|
|
767
1003
|
const languages = Array.isArray(navigation.languages) ? navigation.languages.map(objectValue2).filter((value) => Boolean(value)) : [];
|
|
768
1004
|
let tabs = [];
|
|
769
1005
|
let i18n;
|
|
770
1006
|
if (languages.length > 0) {
|
|
771
|
-
const defaultLanguage = languages.find((entry) => entry.
|
|
1007
|
+
const defaultLanguage = languages.find((entry) => entry.default === true) ?? languages[0];
|
|
772
1008
|
const defaultLocale = String(defaultLanguage.language ?? "en");
|
|
773
1009
|
const locales = languages.map((entry) => {
|
|
774
1010
|
const code = String(entry.language ?? entry.locale ?? "en");
|
|
775
|
-
|
|
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() };
|
|
776
1017
|
});
|
|
777
1018
|
i18n = { defaultLocale, locales };
|
|
778
1019
|
for (const language of languages) {
|
|
779
1020
|
const locale = String(language.language ?? language.locale ?? defaultLocale);
|
|
780
1021
|
const context = {
|
|
781
1022
|
locale,
|
|
1023
|
+
pathPrefix: options.pathPrefix,
|
|
782
1024
|
references,
|
|
783
|
-
seenReferences
|
|
1025
|
+
seenReferences,
|
|
1026
|
+
warnings,
|
|
1027
|
+
warningKeys
|
|
784
1028
|
};
|
|
785
1029
|
const languageTabs = convertContainerToTabs(language, context, "Documentation");
|
|
786
1030
|
if (language === defaultLanguage) tabs = languageTabs;
|
|
787
1031
|
}
|
|
788
1032
|
} else {
|
|
789
|
-
|
|
1033
|
+
const context = { references, seenReferences, warnings, warningKeys, pathPrefix: options.pathPrefix };
|
|
1034
|
+
tabs = convertContainerToTabs(navigation, context, "Documentation");
|
|
790
1035
|
if (tabs.length === 0 && Array.isArray(config.navigation)) {
|
|
791
|
-
const groups = convertGroups(config.navigation,
|
|
1036
|
+
const groups = convertGroups(config.navigation, context, "Overview");
|
|
792
1037
|
if (groups.length > 0) tabs = [{ tab: "Documentation", groups }];
|
|
793
1038
|
}
|
|
794
1039
|
}
|
|
@@ -798,20 +1043,25 @@ function projectMintlifyNavigation(config) {
|
|
|
798
1043
|
message: "Mintlify navigation could not be projected; generated navigation will be used."
|
|
799
1044
|
});
|
|
800
1045
|
}
|
|
801
|
-
if (!tabs.some((tab) => tab.tab.toLowerCase() === "changelog")) {
|
|
802
|
-
tabs.push({ tab: "Changelog", href: "/changelog" });
|
|
803
|
-
}
|
|
804
1046
|
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
805
1047
|
const redirect = objectValue2(value);
|
|
806
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 [];
|
|
807
1052
|
return [{
|
|
808
|
-
source
|
|
809
|
-
destination
|
|
1053
|
+
source,
|
|
1054
|
+
destination,
|
|
810
1055
|
...typeof redirect.permanent === "boolean" ? { permanent: redirect.permanent } : {}
|
|
811
1056
|
}];
|
|
812
1057
|
}) : [];
|
|
813
1058
|
return {
|
|
814
|
-
docsConfig: {
|
|
1059
|
+
docsConfig: {
|
|
1060
|
+
tabs,
|
|
1061
|
+
...projectedCompatibleConfig(config),
|
|
1062
|
+
...i18n ? { i18n } : {},
|
|
1063
|
+
...redirects.length > 0 ? { redirects } : {}
|
|
1064
|
+
},
|
|
815
1065
|
pageReferences: references,
|
|
816
1066
|
warnings
|
|
817
1067
|
};
|
|
@@ -980,12 +1230,50 @@ var DOCUSAURUS_CONFIG_FILENAMES = [
|
|
|
980
1230
|
function hasDocusaurusConfig(directory) {
|
|
981
1231
|
return DOCUSAURUS_CONFIG_FILENAMES.some((filename) => {
|
|
982
1232
|
const path = resolveWithin(directory, filename);
|
|
983
|
-
return existsSync3(path) &&
|
|
1233
|
+
return existsSync3(path) && lstatSync3(path).isFile();
|
|
984
1234
|
});
|
|
985
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
|
+
}
|
|
1247
|
+
});
|
|
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
|
+
}
|
|
986
1274
|
function findDocusaurusProjectRoot(repositoryDir, docsDir) {
|
|
987
1275
|
if (docsDir !== void 0) {
|
|
988
|
-
let candidate = docsDir
|
|
1276
|
+
let candidate = trimTrailingSlashes(docsDir);
|
|
989
1277
|
while (true) {
|
|
990
1278
|
const path = resolveWithin(repositoryDir, candidate || ".");
|
|
991
1279
|
if (hasDocusaurusConfig(path)) return path;
|
|
@@ -1012,14 +1300,14 @@ function findDocusaurusProjectRoot(repositoryDir, docsDir) {
|
|
|
1012
1300
|
return null;
|
|
1013
1301
|
}
|
|
1014
1302
|
function readDocusaurusConfigSource(projectRoot) {
|
|
1015
|
-
const path = DOCUSAURUS_CONFIG_FILENAMES.map((filename) => resolveWithin(projectRoot, filename)).find((candidate) => existsSync3(candidate) &&
|
|
1016
|
-
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 "";
|
|
1017
1305
|
return readFileSync3(path, "utf8");
|
|
1018
1306
|
}
|
|
1019
1307
|
function primaryDocusaurusDocsDirectory(projectRoot) {
|
|
1020
1308
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
1021
1309
|
const configured = source.match(/\bdocs\s*:\s*\{[\s\S]{0,4000}?\bpath\s*:\s*(['"])([^'"]+)\1/)?.[2];
|
|
1022
|
-
return configured?.replace(/^\.\//, "")
|
|
1310
|
+
return trimTrailingSlashes(configured?.replace(/^\.\//, "") ?? "") || "docs";
|
|
1023
1311
|
}
|
|
1024
1312
|
function additionalDocusaurusPluginRoots(repositoryDir, projectRoot) {
|
|
1025
1313
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
@@ -1027,12 +1315,12 @@ function additionalDocusaurusPluginRoots(repositoryDir, projectRoot) {
|
|
|
1027
1315
|
const plugins = [];
|
|
1028
1316
|
const matcher = /['"]@docusaurus\/plugin-content-docs['"][\s\S]{0,3000}?\bpath\s*:\s*(['"])([^'"]+)\1[\s\S]{0,1000}?\brouteBasePath\s*:\s*(['"])([^'"]+)\3/g;
|
|
1029
1317
|
for (const match of source.matchAll(matcher)) {
|
|
1030
|
-
const localPath = match[2].replace(/^\.\//, "")
|
|
1031
|
-
const routePrefix = match[4]
|
|
1318
|
+
const localPath = trimTrailingSlashes(match[2].replace(/^\.\//, ""));
|
|
1319
|
+
const routePrefix = trimEdgeSlashes(match[4]);
|
|
1032
1320
|
if (!localPath || !routePrefix) continue;
|
|
1033
1321
|
const docsDir = [projectRelative, localPath].filter(Boolean).join("/");
|
|
1034
1322
|
const absolute = resolveWithin(repositoryDir, docsDir);
|
|
1035
|
-
if (!existsSync3(absolute) || !
|
|
1323
|
+
if (!existsSync3(absolute) || !lstatSync3(absolute).isDirectory()) continue;
|
|
1036
1324
|
plugins.push({ docsDir, routePrefix });
|
|
1037
1325
|
}
|
|
1038
1326
|
return plugins;
|
|
@@ -1089,13 +1377,13 @@ async function cloneGitHubRepository(source, targetDir) {
|
|
|
1089
1377
|
});
|
|
1090
1378
|
});
|
|
1091
1379
|
}
|
|
1092
|
-
function detectRepositoryPlatform(repositoryDir) {
|
|
1093
|
-
if (
|
|
1094
|
-
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");
|
|
1095
1384
|
if (existsSync3(docsJson)) {
|
|
1096
1385
|
try {
|
|
1097
1386
|
const config = JSON.parse(readFileSync3(docsJson, "utf8"));
|
|
1098
|
-
if (String(config.$schema ?? "").includes("mintlify") || "navigation" in config) return "mintlify";
|
|
1099
1387
|
if (Array.isArray(config.tabs)) return "thally";
|
|
1100
1388
|
} catch {
|
|
1101
1389
|
}
|
|
@@ -1121,7 +1409,7 @@ function containsMarkdown(directory, depth = 0) {
|
|
|
1121
1409
|
function detectRepositoryDocsDir(repositoryDir) {
|
|
1122
1410
|
for (const candidate of ["docs", "documentation", "content", "pages", "src/content", "src/pages", "guide", "guides", ""]) {
|
|
1123
1411
|
const path = resolveWithin(repositoryDir, candidate);
|
|
1124
|
-
if (existsSync3(path) &&
|
|
1412
|
+
if (existsSync3(path) && lstatSync3(path).isDirectory() && containsMarkdown(path)) return candidate;
|
|
1125
1413
|
}
|
|
1126
1414
|
return "";
|
|
1127
1415
|
}
|
|
@@ -1143,30 +1431,135 @@ function scanFiles(root) {
|
|
|
1143
1431
|
function normalizedReferenceKey(value) {
|
|
1144
1432
|
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "").replace(/\/(?:index|readme)$/i, "").replace(/^(?:index|readme)$/i, "") || "introduction";
|
|
1145
1433
|
}
|
|
1434
|
+
function exactReferenceKey(value) {
|
|
1435
|
+
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
1436
|
+
}
|
|
1146
1437
|
function findOpenApi(files) {
|
|
1147
1438
|
return files.find((file) => OPENAPI_FILENAMES.has(basename(file.relativePath).toLowerCase())) ?? null;
|
|
1148
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
|
+
}
|
|
1149
1451
|
function withoutFrontmatter(value) {
|
|
1150
1452
|
return value.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
1151
1453
|
}
|
|
1152
|
-
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()) {
|
|
1153
1547
|
if (depth >= 8) return raw;
|
|
1154
1548
|
const snippets = /* @__PURE__ */ new Map();
|
|
1155
1549
|
const withoutImports = raw.replace(
|
|
1156
1550
|
/^import\s+(?:\{\s*default\s+as\s+)?([A-Z][A-Za-z0-9_]*)\s*\}?\s+from\s+['"]([^'"]+\.mdx?)['"]\s*;?(?:\s*\/\/.*)?$/gm,
|
|
1157
1551
|
(_statement, componentName, sourcePath) => {
|
|
1158
1552
|
try {
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1161
|
-
resolveWithin(repositoryRoot, relative3(repositoryRoot, candidate));
|
|
1162
|
-
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");
|
|
1163
1555
|
const nested = inlineMdxSnippets(
|
|
1164
1556
|
withoutFrontmatter(readFileSync3(candidate, "utf8")),
|
|
1165
1557
|
candidate,
|
|
1166
1558
|
repositoryRoot,
|
|
1167
1559
|
warnings,
|
|
1168
1560
|
depth + 1,
|
|
1169
|
-
siteRoot
|
|
1561
|
+
siteRoot,
|
|
1562
|
+
globalAliases
|
|
1170
1563
|
);
|
|
1171
1564
|
snippets.set(componentName, nested);
|
|
1172
1565
|
return "";
|
|
@@ -1181,26 +1574,40 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
1181
1574
|
}
|
|
1182
1575
|
}
|
|
1183
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
|
+
}
|
|
1184
1590
|
let result = withoutImports;
|
|
1185
1591
|
for (const [componentName, snippet] of snippets) {
|
|
1186
|
-
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
|
+
});
|
|
1187
1597
|
}
|
|
1188
1598
|
return result;
|
|
1189
1599
|
}
|
|
1190
1600
|
function injectOpenApi(config, filename) {
|
|
1191
|
-
const tabs = config.tabs.
|
|
1192
|
-
const apiTab =
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
};
|
|
1196
|
-
const changelog = tabs.findIndex((tab) => tab.tab.toLowerCase() === "changelog");
|
|
1197
|
-
if (changelog >= 0) tabs.splice(changelog, 0, apiTab);
|
|
1198
|
-
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}` } });
|
|
1199
1605
|
return { ...config, tabs };
|
|
1200
1606
|
}
|
|
1201
1607
|
function migrateRepository(options) {
|
|
1202
1608
|
const repositoryDir = options.repositoryDir;
|
|
1203
|
-
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;
|
|
1204
1611
|
const docusaurusProjectRoot = platform === "docusaurus" ? findDocusaurusProjectRoot(repositoryDir, options.docsDir) : null;
|
|
1205
1612
|
const configuredDocsDir = (() => {
|
|
1206
1613
|
if (options.docsDir !== void 0) {
|
|
@@ -1215,23 +1622,30 @@ function migrateRepository(options) {
|
|
|
1215
1622
|
const projectRelative = relative3(repositoryDir, docusaurusProjectRoot).replace(/\\/g, "/");
|
|
1216
1623
|
return [projectRelative, primaryDocusaurusDocsDirectory(docusaurusProjectRoot)].filter(Boolean).join("/");
|
|
1217
1624
|
}
|
|
1625
|
+
if (platform === "mintlify" && mintlifyProjectRoot) {
|
|
1626
|
+
return relative3(repositoryDir, mintlifyProjectRoot).replace(/\\/g, "/");
|
|
1627
|
+
}
|
|
1218
1628
|
return platform === "mintlify" ? "" : detectRepositoryDocsDir(repositoryDir);
|
|
1219
1629
|
})();
|
|
1220
1630
|
const warnings = [];
|
|
1221
1631
|
let docsConfig = { tabs: [] };
|
|
1222
1632
|
const referenceMap = /* @__PURE__ */ new Map();
|
|
1633
|
+
const exactReferenceMap = /* @__PURE__ */ new Map();
|
|
1223
1634
|
const referenceOrder = /* @__PURE__ */ new Map();
|
|
1224
1635
|
let docusaurusSidebars = null;
|
|
1636
|
+
let mintlifyConfig = null;
|
|
1225
1637
|
if (platform === "mintlify") {
|
|
1226
1638
|
try {
|
|
1227
|
-
const config = readMintlifyConfig(repositoryDir);
|
|
1639
|
+
const config = readMintlifyConfig(mintlifyProjectRoot ?? repositoryDir);
|
|
1228
1640
|
if (config) {
|
|
1641
|
+
mintlifyConfig = config;
|
|
1229
1642
|
const projected = projectMintlifyNavigation(config);
|
|
1230
1643
|
docsConfig = projected.docsConfig;
|
|
1231
1644
|
warnings.push(...projected.warnings);
|
|
1232
1645
|
for (const [index, reference] of projected.pageReferences.entries()) {
|
|
1233
1646
|
const key = normalizedReferenceKey(reference.ref);
|
|
1234
1647
|
referenceMap.set(key, reference);
|
|
1648
|
+
exactReferenceMap.set(exactReferenceKey(reference.ref), reference);
|
|
1235
1649
|
referenceOrder.set(key, index);
|
|
1236
1650
|
}
|
|
1237
1651
|
}
|
|
@@ -1253,7 +1667,7 @@ function migrateRepository(options) {
|
|
|
1253
1667
|
}
|
|
1254
1668
|
}
|
|
1255
1669
|
const contentRoot = resolveWithin(repositoryDir, configuredDocsDir);
|
|
1256
|
-
if (!existsSync3(contentRoot) || !
|
|
1670
|
+
if (!existsSync3(contentRoot) || !lstatSync3(contentRoot).isDirectory()) {
|
|
1257
1671
|
throw new Error(`Documentation directory does not exist: ${configuredDocsDir || "."}`);
|
|
1258
1672
|
}
|
|
1259
1673
|
const files = scanFiles(contentRoot);
|
|
@@ -1269,6 +1683,7 @@ function migrateRepository(options) {
|
|
|
1269
1683
|
const rightOrder = referenceOrder.get(normalizedReferenceKey(right.relativePath)) ?? Number.MAX_SAFE_INTEGER;
|
|
1270
1684
|
return leftOrder - rightOrder || left.relativePath.localeCompare(right.relativePath);
|
|
1271
1685
|
});
|
|
1686
|
+
const snippetAliases = platform === "mintlify" && mintlifyProjectRoot ? globalSnippetAliases(files, repositoryDir, mintlifyProjectRoot) : /* @__PURE__ */ new Map();
|
|
1272
1687
|
for (const file of pageFiles) {
|
|
1273
1688
|
if (!isDocumentationExtension(file.relativePath)) continue;
|
|
1274
1689
|
const sourceSegments = file.relativePath.split("/");
|
|
@@ -1277,7 +1692,7 @@ function migrateRepository(options) {
|
|
|
1277
1692
|
continue;
|
|
1278
1693
|
}
|
|
1279
1694
|
const rootFilename = !file.relativePath.includes("/") ? basename(file.relativePath).toLowerCase() : "";
|
|
1280
|
-
if (
|
|
1695
|
+
if (REPOSITORY_ONLY_DOCUMENTS.has(rootFilename) && !exactReferenceMap.has(exactReferenceKey(file.relativePath))) {
|
|
1281
1696
|
skipped++;
|
|
1282
1697
|
continue;
|
|
1283
1698
|
}
|
|
@@ -1286,21 +1701,21 @@ function migrateRepository(options) {
|
|
|
1286
1701
|
warnings.push({ code: "skipped-file", message: "Only Markdown and MDX are imported without an explicit converter.", source: file.relativePath });
|
|
1287
1702
|
continue;
|
|
1288
1703
|
}
|
|
1289
|
-
const size =
|
|
1704
|
+
const size = lstatSync3(file.absolutePath).size;
|
|
1290
1705
|
if (size > MAX_PAGE_BYTES) {
|
|
1291
1706
|
skipped++;
|
|
1292
1707
|
warnings.push({ code: "skipped-file", message: "Page exceeded the 2 MB repository import limit.", source: file.relativePath });
|
|
1293
1708
|
continue;
|
|
1294
1709
|
}
|
|
1295
1710
|
const key = normalizedReferenceKey(file.relativePath);
|
|
1296
|
-
const referenced = referenceMap.get(key);
|
|
1711
|
+
const referenced = exactReferenceMap.get(exactReferenceKey(file.relativePath)) ?? referenceMap.get(key);
|
|
1297
1712
|
let locale = referenced?.locale;
|
|
1298
|
-
let navigationId = referenced?.navigationId ?? pageIdFromReference(file.relativePath);
|
|
1713
|
+
let navigationId = referenced?.navigationId ?? pageIdFromReference(file.relativePath, platform === "mintlify");
|
|
1299
1714
|
if (!referenced && localeConfig) {
|
|
1300
1715
|
const prefix = file.relativePath.split("/", 1)[0];
|
|
1301
1716
|
if (localeConfig.locales.some((entry) => entry.code === prefix)) {
|
|
1302
1717
|
locale = prefix;
|
|
1303
|
-
navigationId = pageIdFromReference(file.relativePath.slice(prefix.length + 1));
|
|
1718
|
+
navigationId = pageIdFromReference(file.relativePath.slice(prefix.length + 1), platform === "mintlify");
|
|
1304
1719
|
}
|
|
1305
1720
|
}
|
|
1306
1721
|
if (!navigationId) {
|
|
@@ -1316,7 +1731,8 @@ function migrateRepository(options) {
|
|
|
1316
1731
|
repositoryDir,
|
|
1317
1732
|
warnings,
|
|
1318
1733
|
0,
|
|
1319
|
-
docusaurusProjectRoot ?? repositoryDir
|
|
1734
|
+
mintlifyProjectRoot ?? docusaurusProjectRoot ?? repositoryDir,
|
|
1735
|
+
snippetAliases
|
|
1320
1736
|
);
|
|
1321
1737
|
let docusaurusDescriptor;
|
|
1322
1738
|
const page = parseMarkdownPage({
|
|
@@ -1324,11 +1740,11 @@ function migrateRepository(options) {
|
|
|
1324
1740
|
navigationId,
|
|
1325
1741
|
...locale ? { locale } : {},
|
|
1326
1742
|
raw,
|
|
1327
|
-
source: `${options.sourceUrl}#${file.
|
|
1743
|
+
source: `${options.sourceUrl}#${relative3(repositoryDir, file.absolutePath).replace(/\\/g, "/")}`,
|
|
1328
1744
|
...platform === "docusaurus" ? {
|
|
1329
1745
|
resolveIdentity: (frontmatter, fallback) => {
|
|
1330
1746
|
const resolved = resolveDocusaurusPageIdentity(file.relativePath, frontmatter, fallback);
|
|
1331
|
-
const routePrefix = options.docusaurusRoutePrefix
|
|
1747
|
+
const routePrefix = options.docusaurusRoutePrefix === void 0 ? void 0 : trimEdgeSlashes(options.docusaurusRoutePrefix);
|
|
1332
1748
|
if (!routePrefix) {
|
|
1333
1749
|
docusaurusDescriptor = resolved.descriptor;
|
|
1334
1750
|
return resolved.identity;
|
|
@@ -1351,6 +1767,9 @@ function migrateRepository(options) {
|
|
|
1351
1767
|
skipped++;
|
|
1352
1768
|
continue;
|
|
1353
1769
|
}
|
|
1770
|
+
if (platform === "mintlify" && mintlifyProjectRoot) {
|
|
1771
|
+
page.body = rewriteRepositoryAssetLinks(page.body, file.absolutePath, mintlifyProjectRoot);
|
|
1772
|
+
}
|
|
1354
1773
|
if (seenPageIds.has(page.id)) {
|
|
1355
1774
|
skipped++;
|
|
1356
1775
|
warnings.push({ code: "collision", message: `Multiple source files map to ${page.id}; the first file was kept.`, source: file.relativePath });
|
|
@@ -1371,7 +1790,7 @@ function migrateRepository(options) {
|
|
|
1371
1790
|
const docusaurusAssetRoot = docusaurusProjectRoot ?? repositoryDir;
|
|
1372
1791
|
const repositoryAssets = platform === "docusaurus" && configuredDocsDir && !options.docusaurusSkipAssets ? ["static", "public"].flatMap((directory) => {
|
|
1373
1792
|
const root = resolveWithin(docusaurusAssetRoot, directory);
|
|
1374
|
-
if (!existsSync3(root) || !
|
|
1793
|
+
if (!existsSync3(root) || !lstatSync3(root).isDirectory()) return [];
|
|
1375
1794
|
return scanFiles(root).map((file) => ({
|
|
1376
1795
|
...file,
|
|
1377
1796
|
relativePath: `${directory}/${file.relativePath}`
|
|
@@ -1380,8 +1799,9 @@ function migrateRepository(options) {
|
|
|
1380
1799
|
let totalAssetBytes = 0;
|
|
1381
1800
|
for (const file of [...files, ...repositoryAssets]) {
|
|
1382
1801
|
const firstSegment = file.relativePath.split("/", 1)[0].toLowerCase();
|
|
1383
|
-
if (!
|
|
1384
|
-
|
|
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;
|
|
1385
1805
|
if (size > MAX_ASSET_BYTES || totalAssetBytes + size > MAX_TOTAL_ASSET_BYTES) {
|
|
1386
1806
|
warnings.push({ code: "limit-reached", message: "An asset was skipped because the migration asset budget was exhausted.", source: file.relativePath });
|
|
1387
1807
|
continue;
|
|
@@ -1419,7 +1839,7 @@ function migrateRepository(options) {
|
|
|
1419
1839
|
}
|
|
1420
1840
|
}
|
|
1421
1841
|
if (docsConfig.tabs.length === 0) docsConfig = buildNavigationFromPages(pages);
|
|
1422
|
-
const openApi = findOpenApi(files);
|
|
1842
|
+
const openApi = findConfiguredMintlifyOpenApi(mintlifyConfig, files) ?? findOpenApi(files);
|
|
1423
1843
|
if (openApi) {
|
|
1424
1844
|
const filename = basename(openApi.relativePath);
|
|
1425
1845
|
if (!assets.some((asset) => asset.path === filename)) {
|
|
@@ -1427,6 +1847,7 @@ function migrateRepository(options) {
|
|
|
1427
1847
|
}
|
|
1428
1848
|
docsConfig = injectOpenApi(docsConfig, filename);
|
|
1429
1849
|
}
|
|
1850
|
+
if (platform === "mintlify") docsConfig = addMintlifyDirectoryRedirects(docsConfig, pages);
|
|
1430
1851
|
if (platform === "docusaurus" && docusaurusProjectRoot && !options.docusaurusSkipPlugins) {
|
|
1431
1852
|
for (const plugin of additionalDocusaurusPluginRoots(repositoryDir, docusaurusProjectRoot)) {
|
|
1432
1853
|
if (plugin.docsDir === configuredDocsDir) continue;
|
|
@@ -1468,6 +1889,12 @@ function migrateRepository(options) {
|
|
|
1468
1889
|
pages,
|
|
1469
1890
|
assets,
|
|
1470
1891
|
docsConfig,
|
|
1892
|
+
...mintlifyConfig ? {
|
|
1893
|
+
site: {
|
|
1894
|
+
...typeof mintlifyConfig.name === "string" ? { name: mintlifyConfig.name } : {},
|
|
1895
|
+
...typeof mintlifyConfig.description === "string" ? { description: mintlifyConfig.description } : {}
|
|
1896
|
+
}
|
|
1897
|
+
} : {},
|
|
1471
1898
|
warnings,
|
|
1472
1899
|
stats: { discovered, imported: pages.length, skipped }
|
|
1473
1900
|
};
|
|
@@ -1481,8 +1908,13 @@ function renderPage(bundle, page) {
|
|
|
1481
1908
|
return [
|
|
1482
1909
|
"---",
|
|
1483
1910
|
`title: ${yamlString(page.title)}`,
|
|
1911
|
+
page.navTitle ? `navTitle: ${yamlString(page.navTitle)}` : null,
|
|
1484
1912
|
`description: ${yamlString(page.description)}`,
|
|
1913
|
+
page.badge ? `badge: ${yamlString(page.badge)}` : null,
|
|
1485
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,
|
|
1486
1918
|
page.openapi ? `openapi: ${yamlString(page.openapi)}` : null,
|
|
1487
1919
|
bundle.sourceKind === "url" ? `source: ${yamlString(page.source)}` : null,
|
|
1488
1920
|
"---",
|
|
@@ -1580,16 +2012,28 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
1580
2012
|
"Color",
|
|
1581
2013
|
"Column",
|
|
1582
2014
|
"Columns",
|
|
2015
|
+
"ContentPanel",
|
|
1583
2016
|
"Danger",
|
|
1584
2017
|
"Error",
|
|
1585
2018
|
"Expandable",
|
|
2019
|
+
"Agent",
|
|
2020
|
+
"AgentPrompt",
|
|
2021
|
+
"BannerPreview",
|
|
2022
|
+
"Embed",
|
|
1586
2023
|
"File",
|
|
1587
2024
|
"Folder",
|
|
1588
2025
|
"Frame",
|
|
2026
|
+
"GitHub",
|
|
2027
|
+
"Github",
|
|
1589
2028
|
"Hero",
|
|
2029
|
+
"Human",
|
|
1590
2030
|
"Icon",
|
|
1591
2031
|
"Info",
|
|
2032
|
+
"InlinePanel",
|
|
2033
|
+
"InlineRequestExample",
|
|
2034
|
+
"InlineResponseExample",
|
|
1592
2035
|
"Latex",
|
|
2036
|
+
"LegacyView",
|
|
1593
2037
|
"Mermaid",
|
|
1594
2038
|
"Note",
|
|
1595
2039
|
"Panel",
|
|
@@ -1604,6 +2048,9 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
1604
2048
|
"Steps",
|
|
1605
2049
|
"Tab",
|
|
1606
2050
|
"Tabs",
|
|
2051
|
+
"Terminal",
|
|
2052
|
+
"TerminalInput",
|
|
2053
|
+
"TerminalOutput",
|
|
1607
2054
|
"Tile",
|
|
1608
2055
|
"TileGroup",
|
|
1609
2056
|
"Tip",
|
|
@@ -1611,6 +2058,7 @@ var PORTABLE_MDX_COMPONENTS = /* @__PURE__ */ new Set([
|
|
|
1611
2058
|
"Tree",
|
|
1612
2059
|
"Update",
|
|
1613
2060
|
"View",
|
|
2061
|
+
"Visibility",
|
|
1614
2062
|
"Warning"
|
|
1615
2063
|
]);
|
|
1616
2064
|
var LOCALE_CODES = /* @__PURE__ */ new Set([
|
|
@@ -1761,7 +2209,7 @@ function pageIdForUrl(url, scopePath) {
|
|
|
1761
2209
|
else if (path === scopePath) path = "";
|
|
1762
2210
|
else if (path.startsWith(`${scopePath}/`)) path = path.slice(scopePath.length + 1);
|
|
1763
2211
|
}
|
|
1764
|
-
return pageIdFromReference(path.replace(/^\/+/, "") || "introduction");
|
|
2212
|
+
return pageIdFromReference(path.replace(/^\/+/, "") || "introduction", true);
|
|
1765
2213
|
}
|
|
1766
2214
|
function canonicalLocalizedPageId(id) {
|
|
1767
2215
|
const [segment, ...rest] = id.split("/");
|
|
@@ -1783,6 +2231,72 @@ function detectUrlPlatform(document) {
|
|
|
1783
2231
|
if (value.includes("starlight")) return "starlight";
|
|
1784
2232
|
return "generic";
|
|
1785
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
|
+
}
|
|
1786
2300
|
function plainDescription(value) {
|
|
1787
2301
|
return value.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1").replace(/[`*_>#-]/g, "").replace(/\s+/g, " ").trim().slice(0, 240);
|
|
1788
2302
|
}
|
|
@@ -2021,7 +2535,12 @@ function remoteMarkdownMetadata(body) {
|
|
|
2021
2535
|
return {
|
|
2022
2536
|
content: body.slice(contentStart),
|
|
2023
2537
|
title: scalar("title"),
|
|
2024
|
-
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
|
|
2025
2544
|
};
|
|
2026
2545
|
}
|
|
2027
2546
|
function stripMintlifyDocumentationIndex(body) {
|
|
@@ -2081,6 +2600,11 @@ function markdownPage(document, id) {
|
|
|
2081
2600
|
"---",
|
|
2082
2601
|
`title: ${JSON.stringify(title)}`,
|
|
2083
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"] : [],
|
|
2084
2608
|
...embeddedOpenApi.openapi ? [`openapi: ${JSON.stringify(embeddedOpenApi.openapi)}`] : [],
|
|
2085
2609
|
"---",
|
|
2086
2610
|
"",
|
|
@@ -2463,7 +2987,7 @@ function htmlTopLevelNavigation(document, source, scopePath) {
|
|
|
2463
2987
|
const collectCandidates = (selector) => {
|
|
2464
2988
|
const candidates2 = [];
|
|
2465
2989
|
$(selector).each((_containerIndex, container) => {
|
|
2466
|
-
const
|
|
2990
|
+
const entries2 = [];
|
|
2467
2991
|
const seenSections = /* @__PURE__ */ new Set();
|
|
2468
2992
|
let anchorCount = 0;
|
|
2469
2993
|
$(container).find("a[href]").each((_index, element) => {
|
|
@@ -2479,22 +3003,40 @@ function htmlTopLevelNavigation(document, source, scopePath) {
|
|
|
2479
3003
|
const section = targetId.split("/", 1)[0] || "introduction";
|
|
2480
3004
|
if (!pageId || seenSections.has(section)) return;
|
|
2481
3005
|
seenSections.add(section);
|
|
2482
|
-
|
|
3006
|
+
entries2.push({ section, label, pageId });
|
|
2483
3007
|
} catch {
|
|
2484
3008
|
}
|
|
2485
3009
|
});
|
|
2486
|
-
if (
|
|
2487
|
-
candidates2.push({ entries, anchorCount });
|
|
3010
|
+
if (entries2.length > 1) {
|
|
3011
|
+
candidates2.push({ entries: entries2, anchorCount });
|
|
2488
3012
|
}
|
|
2489
3013
|
});
|
|
2490
3014
|
return candidates2;
|
|
2491
3015
|
};
|
|
2492
3016
|
const tabCandidates = collectCandidates(".nav-tabs");
|
|
2493
3017
|
const candidates = tabCandidates.length > 0 ? tabCandidates : collectCandidates('nav, [role="navigation"]');
|
|
2494
|
-
|
|
3018
|
+
const entries = candidates.sort((left, right) => {
|
|
2495
3019
|
const densityDifference = right.entries.length / right.anchorCount - left.entries.length / left.anchorCount;
|
|
2496
3020
|
return densityDifference || right.entries.length - left.entries.length;
|
|
2497
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
|
+
};
|
|
2498
3040
|
}
|
|
2499
3041
|
function markdownLinks(body) {
|
|
2500
3042
|
return [...body.matchAll(/\[[^\]]+\]\(([^)\s]+)(?:\s+['"][^'"]*['"])?\)/g)].map((match) => match[1]);
|
|
@@ -2559,8 +3101,13 @@ async function migrateUrl(options) {
|
|
|
2559
3101
|
}
|
|
2560
3102
|
const docusaurusDiscovery = platform === "docusaurus" && docusaurusHtmlProbe ? deriveDocusaurusScope(source, docusaurusHtmlProbe, submittedScopePath) : null;
|
|
2561
3103
|
const scopePath = platform === "mintlify" ? machineIndexScopePath(source, [initial, scopedHtmlProbe]) ?? submittedScopePath : docusaurusDiscovery?.scopePath ?? submittedScopePath;
|
|
2562
|
-
const
|
|
2563
|
-
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);
|
|
2564
3111
|
const cache = /* @__PURE__ */ new Map([[source.toString(), initial]]);
|
|
2565
3112
|
if (docusaurusHtmlProbe) cache.set(docusaurusHtmlProbe.finalUrl.toString(), docusaurusHtmlProbe);
|
|
2566
3113
|
const queue = [source];
|
|
@@ -2581,6 +3128,13 @@ async function migrateUrl(options) {
|
|
|
2581
3128
|
queued.add(candidateIdentity(url));
|
|
2582
3129
|
queue.push(url);
|
|
2583
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
|
+
}
|
|
2584
3138
|
const navigationProbe = docusaurusHtmlProbe ?? scopedHtmlProbe;
|
|
2585
3139
|
if (navigationProbe && /html/i.test(navigationProbe.contentType)) {
|
|
2586
3140
|
for (const link of htmlNavigationLinks(navigationProbe)) enqueue(link, navigationProbe.finalUrl);
|
|
@@ -2784,10 +3338,11 @@ async function migrateUrl(options) {
|
|
|
2784
3338
|
...entry,
|
|
2785
3339
|
pageId: entry.pageId === sourceHomePageId ? "introduction" : entry.pageId
|
|
2786
3340
|
}));
|
|
2787
|
-
|
|
3341
|
+
let docsConfig = platform === "docusaurus" ? docusaurusNavigationFromSnapshots(docusaurusSidebars, pages, scopePath) : hasStructuredMintlifyNavigation && sourceMintlifyProjection ? remapNavigationHome(sourceMintlifyProjection.docsConfig, sourceHomePageId) : buildNavigationFromPages(pages, {
|
|
2788
3342
|
topLevelTabs: platform === "mintlify",
|
|
2789
3343
|
topLevelNavigation
|
|
2790
3344
|
});
|
|
3345
|
+
if (sourceMintlifyProjection) warnings.push(...sourceMintlifyProjection.warnings);
|
|
2791
3346
|
if (docusaurusRedirects.length > 0) {
|
|
2792
3347
|
docsConfig.redirects = [...new Map(docusaurusRedirects.map((redirect) => [
|
|
2793
3348
|
`${redirect.source}:${redirect.destination}`,
|
|
@@ -2817,6 +3372,7 @@ async function migrateUrl(options) {
|
|
|
2817
3372
|
]
|
|
2818
3373
|
};
|
|
2819
3374
|
}
|
|
3375
|
+
if (platform === "mintlify") docsConfig = addMintlifyDirectoryRedirects(docsConfig, pages);
|
|
2820
3376
|
return {
|
|
2821
3377
|
sourceUrl: source.toString(),
|
|
2822
3378
|
sourceKind: "url",
|
|
@@ -2824,6 +3380,12 @@ async function migrateUrl(options) {
|
|
|
2824
3380
|
pages,
|
|
2825
3381
|
assets: openApiAsset ? [{ path: "openapi.yaml", content: openApiAsset }] : [],
|
|
2826
3382
|
docsConfig,
|
|
3383
|
+
...sourceMintlifyConfig ? {
|
|
3384
|
+
site: {
|
|
3385
|
+
...typeof sourceMintlifyConfig.name === "string" ? { name: sourceMintlifyConfig.name } : {},
|
|
3386
|
+
...typeof sourceMintlifyConfig.description === "string" ? { description: sourceMintlifyConfig.description } : {}
|
|
3387
|
+
}
|
|
3388
|
+
} : {},
|
|
2827
3389
|
warnings,
|
|
2828
3390
|
stats: {
|
|
2829
3391
|
discovered: visited.size,
|
|
@@ -2833,6 +3395,7 @@ async function migrateUrl(options) {
|
|
|
2833
3395
|
};
|
|
2834
3396
|
}
|
|
2835
3397
|
export {
|
|
3398
|
+
addMintlifyDirectoryRedirects,
|
|
2836
3399
|
buildNavigationFromPages,
|
|
2837
3400
|
cloneGitHubRepository,
|
|
2838
3401
|
defaultMigrationFetcher,
|