@thallylabs/migrate 0.2.14 → 0.2.18
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 +57 -1
- package/dist/index.d.ts +59 -2
- package/dist/index.js +973 -172
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
statSync
|
|
15
15
|
} from "fs";
|
|
16
16
|
import { createRequire } from "module";
|
|
17
|
-
import { basename as basename3, dirname as dirname4, extname as
|
|
17
|
+
import { basename as basename3, dirname as dirname4, extname as extname6, isAbsolute as isAbsolute2, posix as posix3, relative as relative5, resolve as resolvePath, sep as sep2 } from "path";
|
|
18
18
|
import { parse as parseYaml4 } from "yaml";
|
|
19
19
|
import * as ts3 from "typescript";
|
|
20
20
|
|
|
@@ -326,8 +326,8 @@ function leadingPageHeading(body) {
|
|
|
326
326
|
const root = descriptionParser.parse(body);
|
|
327
327
|
const first = root.children?.find((node) => node.type !== "mdxjsEsm" && node.type !== "html");
|
|
328
328
|
if (first?.type !== "heading" || first.depth !== 1) return void 0;
|
|
329
|
-
const
|
|
330
|
-
const title =
|
|
329
|
+
const text2 = (node) => node.type === "mdxTextExpression" || node.type === "mdxFlowExpression" ? "" : node.value ?? (node.children ?? []).map(text2).join("");
|
|
330
|
+
const title = text2(first).replace(/\s+/g, " ").trim();
|
|
331
331
|
const start = first.position?.start.offset;
|
|
332
332
|
const end = first.position?.end.offset;
|
|
333
333
|
return title && start !== void 0 && end !== void 0 ? { title, start, end } : void 0;
|
|
@@ -400,16 +400,16 @@ function collectEsmBindingsByPattern(source, declared) {
|
|
|
400
400
|
var FENCE_OPEN = /^(`{3,}|~{3,})/;
|
|
401
401
|
function maskInlineSpansForMath(line) {
|
|
402
402
|
const blocks = [];
|
|
403
|
-
const stash = (
|
|
404
|
-
blocks.push(
|
|
403
|
+
const stash = (text2) => {
|
|
404
|
+
blocks.push(text2);
|
|
405
405
|
return `\0${blocks.length - 1}\0`;
|
|
406
406
|
};
|
|
407
407
|
const codeMasked = line.split(/(`[^`]*`)/).map((segment, index) => index % 2 === 1 ? stash(segment) : segment).join("");
|
|
408
408
|
const tagMasked = codeMasked.replace(/<\/?[a-zA-Z][^<>]*>/g, stash);
|
|
409
|
-
const masked = [tagMasked, tagMasked].reduce((
|
|
409
|
+
const masked = [tagMasked, tagMasked].reduce((text2) => text2.replace(/(?<!\$)\{[^{}]*\}/g, stash), tagMasked);
|
|
410
410
|
return {
|
|
411
411
|
masked,
|
|
412
|
-
unmask: (
|
|
412
|
+
unmask: (text2) => text2.replace(/\u0000(\d+)\u0000/g, (_match, index) => blocks[Number(index)])
|
|
413
413
|
};
|
|
414
414
|
}
|
|
415
415
|
var TEX_SIGNAL = /\\[a-zA-Z]|[\^_{]/;
|
|
@@ -562,11 +562,11 @@ function splitFrontmatterBlock(raw) {
|
|
|
562
562
|
return { front: bom + source.slice(0, end), body: source.slice(end) };
|
|
563
563
|
}
|
|
564
564
|
var MIGRATED_COMPONENT_TAG = /^Migrated[0-9a-f]{12}$/;
|
|
565
|
-
function isSelfClosingJsx(
|
|
566
|
-
return /\/>\s*$/.test(
|
|
565
|
+
function isSelfClosingJsx(text2) {
|
|
566
|
+
return /\/>\s*$/.test(text2);
|
|
567
567
|
}
|
|
568
|
-
function renameTagOccurrences(
|
|
569
|
-
let result =
|
|
568
|
+
function renameTagOccurrences(text2, name, newName) {
|
|
569
|
+
let result = text2;
|
|
570
570
|
const openIndex = result.indexOf(`<${name}`);
|
|
571
571
|
if (openIndex >= 0) result = `${result.slice(0, openIndex + 1)}${newName}${result.slice(openIndex + 1 + name.length)}`;
|
|
572
572
|
const closeIndex = result.lastIndexOf(`</${name}`);
|
|
@@ -612,9 +612,9 @@ function replaceUnknownComponents(raw, warn, options = {}) {
|
|
|
612
612
|
if (start === void 0 || end === void 0) return "";
|
|
613
613
|
const isJsx = node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement";
|
|
614
614
|
const tagRoot = node.name ? node.name.split(".")[0] : void 0;
|
|
615
|
-
const
|
|
615
|
+
const text2 = body.slice(start, end);
|
|
616
616
|
if (isJsx && tagRoot === "Link" && !declared.has("Link")) {
|
|
617
|
-
if (isSelfClosingJsx(
|
|
617
|
+
if (isSelfClosingJsx(text2) || !node.children?.length) return renameTagOccurrences(text2, node.name, "a");
|
|
618
618
|
const openEnd = node.children[0].position?.start.offset ?? start;
|
|
619
619
|
const closeStart = node.children.at(-1).position?.end.offset ?? end;
|
|
620
620
|
return renameTagOccurrences(body.slice(start, openEnd), node.name, "a") + renderChildrenSpan(node.children, openEnd, closeStart, body, renderNode) + renameTagOccurrences(body.slice(closeStart, end), node.name, "a");
|
|
@@ -624,7 +624,7 @@ function replaceUnknownComponents(raw, warn, options = {}) {
|
|
|
624
624
|
warned.add(node.name);
|
|
625
625
|
warn(node.name);
|
|
626
626
|
}
|
|
627
|
-
if (isSelfClosingJsx(
|
|
627
|
+
if (isSelfClosingJsx(text2)) return "";
|
|
628
628
|
if (!node.children?.length) return "<div></div>";
|
|
629
629
|
const openEnd = node.children[0].position?.start.offset ?? start;
|
|
630
630
|
const closeStart = node.children.at(-1).position?.end.offset ?? end;
|
|
@@ -635,7 +635,7 @@ ${children}
|
|
|
635
635
|
|
|
636
636
|
</div>` : `<div>${children}</div>`;
|
|
637
637
|
}
|
|
638
|
-
if (!node.children?.length) return
|
|
638
|
+
if (!node.children?.length) return text2;
|
|
639
639
|
return renderChildrenSpan(node.children, start, end, body, renderNode);
|
|
640
640
|
};
|
|
641
641
|
return front + renderNode(root);
|
|
@@ -643,8 +643,8 @@ ${children}
|
|
|
643
643
|
function maskCode(body) {
|
|
644
644
|
const source = body.replace(/\u0000/g, "");
|
|
645
645
|
const blocks = [];
|
|
646
|
-
const stash = (
|
|
647
|
-
blocks.push(
|
|
646
|
+
const stash = (text2) => {
|
|
647
|
+
blocks.push(text2);
|
|
648
648
|
return `\0${blocks.length - 1}\0`;
|
|
649
649
|
};
|
|
650
650
|
const lines = [];
|
|
@@ -671,7 +671,7 @@ function maskCode(body) {
|
|
|
671
671
|
if (fenceBuffer.length) lines.push(stash(fenceBuffer.join("\n")));
|
|
672
672
|
return {
|
|
673
673
|
masked: lines.join("\n"),
|
|
674
|
-
unmask: (
|
|
674
|
+
unmask: (text2) => text2.replace(/\u0000(\d+)\u0000/g, (_match, index) => blocks[Number(index)])
|
|
675
675
|
};
|
|
676
676
|
}
|
|
677
677
|
function replaceOutsideCode(body, transform) {
|
|
@@ -690,7 +690,7 @@ function rewriteFernRelativePageLinks(body, currentSourcePath, routesBySourcePat
|
|
|
690
690
|
const route = candidates.map((candidate) => routesBySourcePath.get(candidate)).find(Boolean);
|
|
691
691
|
return route ? `/${route.replace(/^\/+/, "")}${suffix}` : target;
|
|
692
692
|
};
|
|
693
|
-
return replaceOutsideCode(body, (
|
|
693
|
+
return replaceOutsideCode(body, (text2) => text2.replace(
|
|
694
694
|
/(\]\()(<[^>\n]+>|[^\s)]+)(?=\s|\))/g,
|
|
695
695
|
(_match, opening, target) => `${opening}${target.startsWith("<") && target.endsWith(">") ? `<${rewrite(target.slice(1, -1))}>` : rewrite(target)}`
|
|
696
696
|
).replace(
|
|
@@ -2165,7 +2165,7 @@ const MintlifyComponents = {
|
|
|
2165
2165
|
}
|
|
2166
2166
|
function applyReplacements(source, replacements) {
|
|
2167
2167
|
return replacements.sort((a, b) => b.start - a.start).reduce(
|
|
2168
|
-
(
|
|
2168
|
+
(text2, edit) => text2.slice(0, edit.start) + edit.value + text2.slice(edit.end),
|
|
2169
2169
|
source
|
|
2170
2170
|
);
|
|
2171
2171
|
}
|
|
@@ -2454,14 +2454,14 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2454
2454
|
}
|
|
2455
2455
|
return;
|
|
2456
2456
|
}
|
|
2457
|
-
const
|
|
2458
|
-
const syntax = ts2.transpileModule(
|
|
2457
|
+
const text2 = content.toString("utf8");
|
|
2458
|
+
const syntax = ts2.transpileModule(text2, {
|
|
2459
2459
|
fileName: path,
|
|
2460
2460
|
reportDiagnostics: true,
|
|
2461
2461
|
compilerOptions: { jsx: ts2.JsxEmit.Preserve, target: ts2.ScriptTarget.ESNext, module: ts2.ModuleKind.ESNext }
|
|
2462
2462
|
});
|
|
2463
2463
|
if (syntax.diagnostics?.some((diagnostic) => diagnostic.category === ts2.DiagnosticCategory.Error)) throw new Error("component source contains unsupported JavaScript or TypeScript syntax");
|
|
2464
|
-
const ast = sourceFile(
|
|
2464
|
+
const ast = sourceFile(text2, path);
|
|
2465
2465
|
if (ast.statements.some((statement) => ts2.isExpressionStatement(statement) && ts2.isStringLiteral(statement.expression) && statement.expression.text === "use server")) throw new Error("server-only component modules require manual migration");
|
|
2466
2466
|
const edits = [];
|
|
2467
2467
|
function dependency(literal, importDeclaration) {
|
|
@@ -2509,7 +2509,7 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2509
2509
|
ts2.forEachChild(node, inspect);
|
|
2510
2510
|
}
|
|
2511
2511
|
inspect(ast);
|
|
2512
|
-
const replaced = applyReplacements(
|
|
2512
|
+
const replaced = applyReplacements(text2, edits);
|
|
2513
2513
|
const shebangMatch = /^#!.*\r?\n/.exec(replaced);
|
|
2514
2514
|
const prologue = "// @ts-nocheck\n'use client';\n\n";
|
|
2515
2515
|
staged.set(destination, {
|
|
@@ -3016,10 +3016,10 @@ ${inlineSource}` });
|
|
|
3016
3016
|
const start = node.position.start.offset;
|
|
3017
3017
|
const end = node.position.end.offset;
|
|
3018
3018
|
if (extracted.some((item) => start >= item.node.position.start.offset && end <= item.node.position.end.offset)) return;
|
|
3019
|
-
const
|
|
3020
|
-
const opening =
|
|
3019
|
+
const text2 = content.slice(start, end);
|
|
3020
|
+
const opening = text2.indexOf(`<${node.name}`);
|
|
3021
3021
|
if (opening >= 0) edits.push({ start: start + opening + 1, end: start + opening + 1 + node.name.length, value: replacement });
|
|
3022
|
-
const closing =
|
|
3022
|
+
const closing = text2.lastIndexOf(`</${node.name}`);
|
|
3023
3023
|
if (closing >= 0) edits.push({ start: start + closing + 2, end: start + closing + 2 + node.name.length, value: replacement });
|
|
3024
3024
|
});
|
|
3025
3025
|
walk(tree, (node) => {
|
|
@@ -3104,13 +3104,96 @@ import { customComponents as ${name} } from ${JSON.stringify(registryPath)}
|
|
|
3104
3104
|
|
|
3105
3105
|
// src/docusaurus.ts
|
|
3106
3106
|
import { existsSync as existsSync3, lstatSync as lstatSync3, readFileSync as readFileSync3 } from "fs";
|
|
3107
|
-
import { extname as
|
|
3107
|
+
import { extname as extname4, posix as posix2 } from "path";
|
|
3108
3108
|
import JSON5 from "json5";
|
|
3109
3109
|
import { parse as parseYaml2 } from "yaml";
|
|
3110
3110
|
|
|
3111
3111
|
// src/navigation.ts
|
|
3112
3112
|
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
3113
|
-
import { dirname as dirname2, extname as
|
|
3113
|
+
import { dirname as dirname2, extname as extname3, relative as relative3 } from "path";
|
|
3114
|
+
|
|
3115
|
+
// src/mintlify-extras.ts
|
|
3116
|
+
import { extname as extname2 } from "path";
|
|
3117
|
+
function objectValue(value) {
|
|
3118
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
3119
|
+
}
|
|
3120
|
+
var PUBLIC_FALSE_STRINGS = /* @__PURE__ */ new Set(["false", "no", "off", "0"]);
|
|
3121
|
+
function isPublicFalse(value) {
|
|
3122
|
+
return value === false || value === 0 || typeof value === "string" && PUBLIC_FALSE_STRINGS.has(value.trim().toLowerCase());
|
|
3123
|
+
}
|
|
3124
|
+
function isPublicTrue(value) {
|
|
3125
|
+
return value === true || typeof value === "string" && value.trim().toLowerCase() === "true";
|
|
3126
|
+
}
|
|
3127
|
+
function frontmatterGateReason(data) {
|
|
3128
|
+
const groups = data.groups;
|
|
3129
|
+
const hasGroups = Array.isArray(groups) ? groups.length > 0 : typeof groups === "string" ? groups.trim() !== "" : groups !== void 0 && groups !== null && groups !== false;
|
|
3130
|
+
if (hasGroups) return "frontmatter `groups` restricts it to authenticated groups";
|
|
3131
|
+
if (isPublicFalse(data.public)) return "frontmatter `public: false`";
|
|
3132
|
+
return void 0;
|
|
3133
|
+
}
|
|
3134
|
+
function navigationGateReason(node) {
|
|
3135
|
+
const groups = node.groups;
|
|
3136
|
+
const isContainer = (entry) => typeof entry === "object" && entry !== null && !Array.isArray(entry) && ["group", "pages", "tab", "groups", "page", "root", "menu", "href"].some((key) => key in entry);
|
|
3137
|
+
const restricted = typeof groups === "string" ? groups.trim() !== "" : Array.isArray(groups) && groups.length > 0 && !groups.every(isContainer);
|
|
3138
|
+
if (restricted) return "its navigation container sets `groups`";
|
|
3139
|
+
if (isPublicFalse(node.public)) return "its navigation container sets `public: false`";
|
|
3140
|
+
return void 0;
|
|
3141
|
+
}
|
|
3142
|
+
var CONFIG_BASENAME = /(?:^|[.-])(?:config|rc)\.[cm]?js$|^(?:tailwind|postcss|next|eslint|prettier|babel|webpack|vite|vitest|jest|rollup|gulpfile|gruntfile|stylelint|commitlint|lint-staged|svgo|metro)[.-]/i;
|
|
3143
|
+
var BUILD_OUTPUT_SEGMENTS = /* @__PURE__ */ new Set(["dist", "build", "coverage"]);
|
|
3144
|
+
var FONT_EXTENSIONS = /* @__PURE__ */ new Set([".woff", ".woff2", ".ttf", ".otf"]);
|
|
3145
|
+
function isMintlifyServedScriptOrStyle(relativePath) {
|
|
3146
|
+
const extension = extname2(relativePath).toLowerCase();
|
|
3147
|
+
if (extension !== ".css" && extension !== ".js") return false;
|
|
3148
|
+
const segments = relativePath.split("/");
|
|
3149
|
+
if (segments.slice(0, -1).some((segment) => BUILD_OUTPUT_SEGMENTS.has(segment.toLowerCase()))) return false;
|
|
3150
|
+
return extension === ".css" || !CONFIG_BASENAME.test(segments[segments.length - 1]);
|
|
3151
|
+
}
|
|
3152
|
+
function mintlifyFontSources(config) {
|
|
3153
|
+
const fonts = objectValue(config?.fonts);
|
|
3154
|
+
if (!fonts) return [];
|
|
3155
|
+
const found = [];
|
|
3156
|
+
for (const entry of [fonts, objectValue(fonts.heading), objectValue(fonts.body)]) {
|
|
3157
|
+
if (!entry || typeof entry.source !== "string" || !entry.source.trim()) continue;
|
|
3158
|
+
const source = entry.source.trim();
|
|
3159
|
+
const family = typeof entry.family === "string" ? entry.family : "a font";
|
|
3160
|
+
const remote = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(source);
|
|
3161
|
+
const path = remote || /^[a-z]:[\\/]/i.test(source) ? null : normalizeAssetPath(source);
|
|
3162
|
+
found.push({ family, source, path: path && FONT_EXTENSIONS.has(extname2(path).toLowerCase()) ? path : null, remote });
|
|
3163
|
+
}
|
|
3164
|
+
return found;
|
|
3165
|
+
}
|
|
3166
|
+
function mintlifyAppearance(config, warnings) {
|
|
3167
|
+
const appearance = objectValue(config.appearance);
|
|
3168
|
+
const legacy = objectValue(config.modeToggle);
|
|
3169
|
+
const result = {};
|
|
3170
|
+
const isMode = (value) => value === "system" || value === "light" || value === "dark";
|
|
3171
|
+
const skip = (field, value) => {
|
|
3172
|
+
warnings.push({
|
|
3173
|
+
code: "unsupported-config",
|
|
3174
|
+
message: `Ignored invalid ${field} value ${JSON.stringify(value)?.slice(0, 40)}; the appearance default was left unchanged.`
|
|
3175
|
+
});
|
|
3176
|
+
};
|
|
3177
|
+
if (appearance?.default !== void 0) {
|
|
3178
|
+
if (isMode(appearance.default)) result.default = appearance.default;
|
|
3179
|
+
else skip("appearance.default", appearance.default);
|
|
3180
|
+
} else if (legacy?.default !== void 0) {
|
|
3181
|
+
if (isMode(legacy.default)) result.default = legacy.default;
|
|
3182
|
+
else skip("modeToggle.default", legacy.default);
|
|
3183
|
+
}
|
|
3184
|
+
if (appearance?.strict !== void 0) {
|
|
3185
|
+
if (typeof appearance.strict === "boolean") {
|
|
3186
|
+
if (appearance.strict) result.showToggle = false;
|
|
3187
|
+
} else skip("appearance.strict", appearance.strict);
|
|
3188
|
+
} else if (legacy?.isHidden !== void 0) {
|
|
3189
|
+
if (typeof legacy.isHidden === "boolean") {
|
|
3190
|
+
if (legacy.isHidden) result.showToggle = false;
|
|
3191
|
+
} else skip("modeToggle.isHidden", legacy.isHidden);
|
|
3192
|
+
}
|
|
3193
|
+
return result;
|
|
3194
|
+
}
|
|
3195
|
+
|
|
3196
|
+
// src/navigation.ts
|
|
3114
3197
|
var MAX_MINTLIFY_CONFIG_BYTES = 2e7;
|
|
3115
3198
|
function addMintlifyDirectoryRedirects(config, pages) {
|
|
3116
3199
|
const pageIds = new Set(pages.map((page) => page.id));
|
|
@@ -3210,13 +3293,13 @@ var LANGUAGE_LABELS = {
|
|
|
3210
3293
|
uk: "Ukrainian",
|
|
3211
3294
|
zh: "Chinese"
|
|
3212
3295
|
};
|
|
3213
|
-
function
|
|
3296
|
+
function objectValue2(value) {
|
|
3214
3297
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
3215
3298
|
}
|
|
3216
3299
|
function withFlattenedProductGroups(container) {
|
|
3217
3300
|
if (!Array.isArray(container.productGroups)) return container;
|
|
3218
3301
|
const flattened = container.productGroups.flatMap((entry) => {
|
|
3219
|
-
const group =
|
|
3302
|
+
const group = objectValue2(entry);
|
|
3220
3303
|
return group && Array.isArray(group.products) ? group.products : [];
|
|
3221
3304
|
});
|
|
3222
3305
|
if (flattened.length === 0) return container;
|
|
@@ -3251,7 +3334,7 @@ function jsonPointer(root, pointer) {
|
|
|
3251
3334
|
const index = Number(token);
|
|
3252
3335
|
return Number.isSafeInteger(index) && index >= 0 ? current[index] : void 0;
|
|
3253
3336
|
}
|
|
3254
|
-
const object =
|
|
3337
|
+
const object = objectValue2(current);
|
|
3255
3338
|
if (!object) return void 0;
|
|
3256
3339
|
return object[token.replace(/~1/g, "/").replace(/~0/g, "~")];
|
|
3257
3340
|
}, root);
|
|
@@ -3272,7 +3355,7 @@ function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
|
3272
3355
|
if (Array.isArray(value)) {
|
|
3273
3356
|
return value.map((entry) => resolveJsonReferences(entry, currentFile, repositoryRoot, stack));
|
|
3274
3357
|
}
|
|
3275
|
-
const object =
|
|
3358
|
+
const object = objectValue2(value);
|
|
3276
3359
|
if (!object) return value;
|
|
3277
3360
|
if (typeof object.$ref === "string") {
|
|
3278
3361
|
const [filePart, pointer = ""] = object.$ref.split("#", 2);
|
|
@@ -3292,7 +3375,7 @@ function resolveJsonReferences(value, currentFile, repositoryRoot, stack) {
|
|
|
3292
3375
|
resolveJsonReferences(entry, currentFile, repositoryRoot, stack)
|
|
3293
3376
|
])
|
|
3294
3377
|
);
|
|
3295
|
-
return
|
|
3378
|
+
return objectValue2(resolved) ? { ...resolved, ...siblings } : resolved;
|
|
3296
3379
|
}
|
|
3297
3380
|
return Object.fromEntries(
|
|
3298
3381
|
Object.entries(object).map(([key, entry]) => [
|
|
@@ -3308,19 +3391,19 @@ function readMintlifyConfig(repositoryRoot) {
|
|
|
3308
3391
|
return resolveJsonReferences(raw, configPath, repositoryRoot, /* @__PURE__ */ new Set());
|
|
3309
3392
|
}
|
|
3310
3393
|
function specSource(value) {
|
|
3311
|
-
const source = typeof value === "string" ? value :
|
|
3394
|
+
const source = typeof value === "string" ? value : objectValue2(value)?.source;
|
|
3312
3395
|
return typeof source === "string" && source.trim() ? source.trim() : void 0;
|
|
3313
3396
|
}
|
|
3314
3397
|
var hasNavigationChildren = (node) => ["groups", "pages"].some((key) => Array.isArray(node[key]) && node[key].length > 0);
|
|
3315
3398
|
function mintlifyNavigationApiReferences(config) {
|
|
3316
|
-
const navigation =
|
|
3399
|
+
const navigation = objectValue2(config.navigation) ?? config;
|
|
3317
3400
|
const references = [];
|
|
3318
3401
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus", "languages"];
|
|
3319
3402
|
const nestedKeys = ["groups", "pages"];
|
|
3320
3403
|
function collect(node, tabLabel, sibling) {
|
|
3321
3404
|
for (const kind of ["openapi", "asyncapi"]) {
|
|
3322
3405
|
const value = specSource(node[kind]);
|
|
3323
|
-
const directory =
|
|
3406
|
+
const directory = objectValue2(node[kind])?.directory;
|
|
3324
3407
|
if (value) {
|
|
3325
3408
|
references.push({
|
|
3326
3409
|
value,
|
|
@@ -3345,7 +3428,7 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
3345
3428
|
const entries = node[key];
|
|
3346
3429
|
if (!Array.isArray(entries)) continue;
|
|
3347
3430
|
for (const entry of entries) {
|
|
3348
|
-
const object =
|
|
3431
|
+
const object = objectValue2(entry);
|
|
3349
3432
|
if (!object) continue;
|
|
3350
3433
|
visit(object, labelFor(object, tabLabel ?? "Documentation"));
|
|
3351
3434
|
}
|
|
@@ -3355,14 +3438,14 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
3355
3438
|
if (!Array.isArray(entries)) continue;
|
|
3356
3439
|
for (const entry of entries) {
|
|
3357
3440
|
if (typeof entry === "string") continue;
|
|
3358
|
-
const object =
|
|
3441
|
+
const object = objectValue2(entry);
|
|
3359
3442
|
if (object) visit(object, tabLabel);
|
|
3360
3443
|
}
|
|
3361
3444
|
}
|
|
3362
3445
|
if (Array.isArray(node.menu)) {
|
|
3363
3446
|
const tab = tabLabel ?? "Documentation";
|
|
3364
3447
|
const items = node.menu.flatMap((entry) => {
|
|
3365
|
-
const object =
|
|
3448
|
+
const object = objectValue2(entry);
|
|
3366
3449
|
return object ? [object] : [];
|
|
3367
3450
|
});
|
|
3368
3451
|
const apiItems = items.filter((item) => specSource(item.openapi) || specSource(item.asyncapi));
|
|
@@ -3408,6 +3491,23 @@ function normalizePageRef(value, pathPrefix = "") {
|
|
|
3408
3491
|
ref = ref.replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
3409
3492
|
return pageIdFromReference(ref, true);
|
|
3410
3493
|
}
|
|
3494
|
+
function collectGatedReferences(node, gate, pathPrefix, out, depth = 0) {
|
|
3495
|
+
if (depth > 32) return;
|
|
3496
|
+
if (Array.isArray(node)) {
|
|
3497
|
+
for (const item of node) collectGatedReferences(item, gate, pathPrefix, out, depth + 1);
|
|
3498
|
+
return;
|
|
3499
|
+
}
|
|
3500
|
+
const object = objectValue2(node);
|
|
3501
|
+
if (!object) return;
|
|
3502
|
+
const reason = gate ?? navigationGateReason(object);
|
|
3503
|
+
if (reason) {
|
|
3504
|
+
const refs = [object.page, object.root, ...Array.isArray(object.pages) ? object.pages : []];
|
|
3505
|
+
for (const ref of refs) {
|
|
3506
|
+
if (typeof ref === "string" && normalizePageRef(ref, pathPrefix)) out.push({ ref, reason });
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
for (const value of Object.values(object)) collectGatedReferences(value, reason, pathPrefix, out, depth + 1);
|
|
3510
|
+
}
|
|
3411
3511
|
function warnOnce(context, key, message) {
|
|
3412
3512
|
if (context.warningKeys.has(key)) return;
|
|
3413
3513
|
context.warningKeys.add(key);
|
|
@@ -3415,7 +3515,7 @@ function warnOnce(context, key, message) {
|
|
|
3415
3515
|
}
|
|
3416
3516
|
function iconName(value) {
|
|
3417
3517
|
if (typeof value === "string" && value) return value;
|
|
3418
|
-
const icon =
|
|
3518
|
+
const icon = objectValue2(value);
|
|
3419
3519
|
return typeof icon?.name === "string" && icon.name ? icon.name : void 0;
|
|
3420
3520
|
}
|
|
3421
3521
|
function containerPresentation(value) {
|
|
@@ -3425,6 +3525,10 @@ function containerPresentation(value) {
|
|
|
3425
3525
|
};
|
|
3426
3526
|
}
|
|
3427
3527
|
function registerReference(value, context) {
|
|
3528
|
+
if (context.gateReason) {
|
|
3529
|
+
if (normalizePageRef(value, context.pathPrefix)) context.gated.push({ ref: value, reason: context.gateReason });
|
|
3530
|
+
return null;
|
|
3531
|
+
}
|
|
3428
3532
|
const localizedValue = context.locale ? mintlifyLocalizedReference(value, context.locale, context.defaultPageIds) : value;
|
|
3429
3533
|
const navigationId = normalizePageRef(localizedValue, context.pathPrefix);
|
|
3430
3534
|
if (!navigationId) return null;
|
|
@@ -3448,13 +3552,24 @@ function convertPage(value, context) {
|
|
|
3448
3552
|
}
|
|
3449
3553
|
return registerReference(value, context);
|
|
3450
3554
|
}
|
|
3451
|
-
const object =
|
|
3555
|
+
const object = objectValue2(value);
|
|
3452
3556
|
if (!object) return null;
|
|
3557
|
+
const outerGate = context.gateReason;
|
|
3558
|
+
const gate = navigationGateReason(object);
|
|
3559
|
+
if (gate && !outerGate) context.gateReason = gate;
|
|
3560
|
+
try {
|
|
3561
|
+
return convertPageObject(object, context);
|
|
3562
|
+
} finally {
|
|
3563
|
+
context.gateReason = outerGate;
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
function convertPageObject(object, context) {
|
|
3453
3567
|
if (typeof object.page === "string") return registerReference(object.page, context);
|
|
3454
3568
|
const href = projectedHref(object.href);
|
|
3455
3569
|
if (href && !object.pages && !object.groups) {
|
|
3456
3570
|
const page = registerReference(href, context);
|
|
3457
3571
|
if (page) return page;
|
|
3572
|
+
if (context.gateReason) return null;
|
|
3458
3573
|
warnOnce(
|
|
3459
3574
|
context,
|
|
3460
3575
|
"external-page-link",
|
|
@@ -3499,7 +3614,7 @@ var KNOWN_NAVIGATION_KEYS = /* @__PURE__ */ new Set([
|
|
|
3499
3614
|
]);
|
|
3500
3615
|
function convertMenuItems(items, context) {
|
|
3501
3616
|
return items.flatMap((value, index) => {
|
|
3502
|
-
const item =
|
|
3617
|
+
const item = objectValue2(value);
|
|
3503
3618
|
if (!item) return [];
|
|
3504
3619
|
const label = labelFor(item, `Menu item ${index + 1}`);
|
|
3505
3620
|
if (typeof item.description === "string" && item.description.trim()) {
|
|
@@ -3549,14 +3664,14 @@ function convertNavigationValues(values, context) {
|
|
|
3549
3664
|
});
|
|
3550
3665
|
}
|
|
3551
3666
|
function convertContainerToTabs(containerValue, context, fallbackTab, trace, depth = 0) {
|
|
3552
|
-
const rawContainer =
|
|
3667
|
+
const rawContainer = objectValue2(containerValue);
|
|
3553
3668
|
if (!rawContainer) return [];
|
|
3554
3669
|
const container = withFlattenedProductGroups(rawContainer);
|
|
3555
3670
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus"];
|
|
3556
3671
|
for (const key of Object.keys(container)) {
|
|
3557
3672
|
if (KNOWN_NAVIGATION_KEYS.has(key)) continue;
|
|
3558
3673
|
const entries = container[key];
|
|
3559
|
-
if (Array.isArray(entries) && entries.some((entry) =>
|
|
3674
|
+
if (Array.isArray(entries) && entries.some((entry) => objectValue2(entry))) {
|
|
3560
3675
|
warnOnce(context, `unknown-container:${key}`, `Mintlify navigation key "${key}" is not supported and its contents were not projected.`);
|
|
3561
3676
|
}
|
|
3562
3677
|
}
|
|
@@ -3564,10 +3679,10 @@ function convertContainerToTabs(containerValue, context, fallbackTab, trace, dep
|
|
|
3564
3679
|
if (!Array.isArray(container[key])) continue;
|
|
3565
3680
|
const entries = [...container[key]];
|
|
3566
3681
|
if (key === "versions") {
|
|
3567
|
-
entries.sort((left, right) => Number(
|
|
3682
|
+
entries.sort((left, right) => Number(objectValue2(right)?.default === true) - Number(objectValue2(left)?.default === true));
|
|
3568
3683
|
}
|
|
3569
3684
|
const tabs = entries.flatMap((value, index) => {
|
|
3570
|
-
const object =
|
|
3685
|
+
const object = objectValue2(value);
|
|
3571
3686
|
if (!object) return [];
|
|
3572
3687
|
const tab = labelFor(object, `${fallbackTab} ${index + 1}`);
|
|
3573
3688
|
const href = projectedHref(object.href);
|
|
@@ -3580,7 +3695,15 @@ function convertContainerToTabs(containerValue, context, fallbackTab, trace, dep
|
|
|
3580
3695
|
...object.hidden === true ? { hidden: true } : {}
|
|
3581
3696
|
}];
|
|
3582
3697
|
}
|
|
3583
|
-
const
|
|
3698
|
+
const outerGate = context.gateReason;
|
|
3699
|
+
const gate = navigationGateReason(object);
|
|
3700
|
+
if (gate && !outerGate) context.gateReason = gate;
|
|
3701
|
+
let nested;
|
|
3702
|
+
try {
|
|
3703
|
+
nested = convertContainerToTabs(object, context, tab, trace, depth + 1);
|
|
3704
|
+
} finally {
|
|
3705
|
+
context.gateReason = outerGate;
|
|
3706
|
+
}
|
|
3584
3707
|
if (nested.length > 0) {
|
|
3585
3708
|
if (nested.length === 1) {
|
|
3586
3709
|
return [{
|
|
@@ -3639,17 +3762,17 @@ function projectedTheme(value) {
|
|
|
3639
3762
|
return typeof value === "string" ? "default" : void 0;
|
|
3640
3763
|
}
|
|
3641
3764
|
function projectedNavbar(value) {
|
|
3642
|
-
const navbar =
|
|
3765
|
+
const navbar = objectValue2(value);
|
|
3643
3766
|
if (!navbar) return void 0;
|
|
3644
3767
|
const links = Array.isArray(navbar.links) ? navbar.links.flatMap((entry) => {
|
|
3645
|
-
const link =
|
|
3768
|
+
const link = objectValue2(entry);
|
|
3646
3769
|
const href = projectedHref(link?.href);
|
|
3647
3770
|
if (!link || !href) return [];
|
|
3648
3771
|
const label = typeof link.label === "string" ? link.label : typeof link.name === "string" ? link.name : "";
|
|
3649
3772
|
if (!label) return [];
|
|
3650
3773
|
return [{ label, href, ...link.type === "github" ? { type: "github" } : {} }];
|
|
3651
3774
|
}) : [];
|
|
3652
|
-
const primaryValue =
|
|
3775
|
+
const primaryValue = objectValue2(navbar.primary);
|
|
3653
3776
|
const primaryHref = projectedHref(primaryValue?.href);
|
|
3654
3777
|
const primary = primaryValue && primaryHref ? {
|
|
3655
3778
|
label: typeof primaryValue.label === "string" ? primaryValue.label : primaryValue.type === "github" ? "GitHub" : "Get started",
|
|
@@ -3657,8 +3780,22 @@ function projectedNavbar(value) {
|
|
|
3657
3780
|
} : void 0;
|
|
3658
3781
|
return links.length > 0 || primary ? { ...links.length > 0 ? { links } : {}, ...primary ? { primary } : {} } : void 0;
|
|
3659
3782
|
}
|
|
3783
|
+
function legacyTopbarNavbar(config) {
|
|
3784
|
+
const links = Array.isArray(config.topbarLinks) ? config.topbarLinks.flatMap((entry) => {
|
|
3785
|
+
const link = objectValue2(entry);
|
|
3786
|
+
if (!link) return [];
|
|
3787
|
+
return [{
|
|
3788
|
+
label: link.name ?? link.label ?? (link.type === "github" ? "GitHub" : void 0),
|
|
3789
|
+
href: link.url ?? link.href,
|
|
3790
|
+
...link.type === "github" ? { type: "github" } : {}
|
|
3791
|
+
}];
|
|
3792
|
+
}) : [];
|
|
3793
|
+
const cta = objectValue2(config.topbarCtaButton);
|
|
3794
|
+
const primary = cta ? { label: cta.name ?? cta.label, href: cta.url ?? cta.href, type: cta.type } : void 0;
|
|
3795
|
+
return links.length > 0 || primary ? { ...links.length > 0 ? { links } : {}, ...primary ? { primary } : {} } : void 0;
|
|
3796
|
+
}
|
|
3660
3797
|
function projectedGlobalNavigationLinks(value) {
|
|
3661
|
-
const global =
|
|
3798
|
+
const global = objectValue2(value);
|
|
3662
3799
|
if (!global) return [];
|
|
3663
3800
|
const links = [];
|
|
3664
3801
|
const visit = (entry) => {
|
|
@@ -3666,7 +3803,7 @@ function projectedGlobalNavigationLinks(value) {
|
|
|
3666
3803
|
entry.forEach(visit);
|
|
3667
3804
|
return;
|
|
3668
3805
|
}
|
|
3669
|
-
const item =
|
|
3806
|
+
const item = objectValue2(entry);
|
|
3670
3807
|
if (!item) return;
|
|
3671
3808
|
const href = projectedHref(item.href);
|
|
3672
3809
|
if (href) {
|
|
@@ -3681,19 +3818,19 @@ function projectedGlobalNavigationLinks(value) {
|
|
|
3681
3818
|
return links;
|
|
3682
3819
|
}
|
|
3683
3820
|
function projectedFooter(value) {
|
|
3684
|
-
const footer =
|
|
3821
|
+
const footer = objectValue2(value);
|
|
3685
3822
|
if (!footer) return void 0;
|
|
3686
|
-
const socials =
|
|
3823
|
+
const socials = objectValue2(footer.socials);
|
|
3687
3824
|
const socialLinks = socials ? Object.fromEntries(Object.entries(socials).flatMap(([key, value2]) => {
|
|
3688
3825
|
const href = projectedHref(value2);
|
|
3689
3826
|
return href ? [[key, href]] : [];
|
|
3690
3827
|
})) : void 0;
|
|
3691
3828
|
const links = Array.isArray(footer.links) ? footer.links.flatMap((entry) => {
|
|
3692
|
-
const column =
|
|
3829
|
+
const column = objectValue2(entry);
|
|
3693
3830
|
if (!column || !Array.isArray(column.items)) return [];
|
|
3694
3831
|
const heading = typeof column.heading === "string" ? column.heading : typeof column.header === "string" ? column.header : "";
|
|
3695
3832
|
const items = column.items.flatMap((item) => {
|
|
3696
|
-
const link =
|
|
3833
|
+
const link = objectValue2(item);
|
|
3697
3834
|
const href = projectedHref(link?.href);
|
|
3698
3835
|
return link && typeof link.label === "string" && href ? [{ label: link.label, href }] : [];
|
|
3699
3836
|
});
|
|
@@ -3703,26 +3840,45 @@ function projectedFooter(value) {
|
|
|
3703
3840
|
}
|
|
3704
3841
|
function projectedFont(value) {
|
|
3705
3842
|
if (typeof value === "string" && value) return { family: value };
|
|
3706
|
-
const font =
|
|
3843
|
+
const font = objectValue2(value);
|
|
3707
3844
|
if (!font) return void 0;
|
|
3708
3845
|
const family = typeof font.family === "string" ? font.family : typeof font.name === "string" ? font.name : void 0;
|
|
3709
3846
|
if (!family) return void 0;
|
|
3710
3847
|
const weight = Array.isArray(font.weight) ? font.weight.map(String) : Array.isArray(font.weights) ? font.weights.map(String) : void 0;
|
|
3711
3848
|
return { family, ...weight && weight.length > 0 ? { weight } : {} };
|
|
3712
3849
|
}
|
|
3713
|
-
function projectedCompatibleConfig(config) {
|
|
3714
|
-
const banner =
|
|
3715
|
-
const bannerContent = banner && (typeof banner.content === "string" ||
|
|
3850
|
+
function projectedCompatibleConfig(config, warnings) {
|
|
3851
|
+
const banner = objectValue2(config.banner);
|
|
3852
|
+
const bannerContent = banner && (typeof banner.content === "string" || objectValue2(banner.content)) ? banner.content : void 0;
|
|
3716
3853
|
const bannerType = banner && ["info", "warning", "critical"].includes(String(banner.type ?? banner.variant)) ? String(banner.type ?? banner.variant) : void 0;
|
|
3717
|
-
const bannerColor =
|
|
3718
|
-
const bodyFont = projectedFont(
|
|
3719
|
-
const headingFont = projectedFont(
|
|
3720
|
-
const feedback =
|
|
3721
|
-
const seo =
|
|
3722
|
-
const iconLibrary =
|
|
3854
|
+
const bannerColor = objectValue2(banner?.color);
|
|
3855
|
+
const bodyFont = projectedFont(objectValue2(config.fonts)?.body ?? config.fonts);
|
|
3856
|
+
const headingFont = projectedFont(objectValue2(config.fonts)?.heading);
|
|
3857
|
+
const feedback = objectValue2(config.feedback);
|
|
3858
|
+
const seo = objectValue2(config.seo);
|
|
3859
|
+
const iconLibrary = objectValue2(config.icons)?.library;
|
|
3723
3860
|
const projectedIconLibrary = ["lucide", "fontawesome", "tabler"].includes(String(iconLibrary)) ? String(iconLibrary) : void 0;
|
|
3724
|
-
const
|
|
3725
|
-
const
|
|
3861
|
+
const docsNavbar = projectedNavbar(config.navbar);
|
|
3862
|
+
const legacyRaw = legacyTopbarNavbar(config);
|
|
3863
|
+
const legacyNavbar = projectedNavbar(legacyRaw);
|
|
3864
|
+
const legacyLinkCount = Array.isArray(legacyRaw?.links) ? legacyRaw.links.length : 0;
|
|
3865
|
+
if (legacyNavbar?.links && legacyNavbar.links.length < legacyLinkCount) {
|
|
3866
|
+
warnings.push({ code: "unsupported-config", message: `${legacyLinkCount - legacyNavbar.links.length} mint.json topbarLinks entr${legacyLinkCount - legacyNavbar.links.length === 1 ? "y" : "ies"} without a valid name and url were skipped.` });
|
|
3867
|
+
}
|
|
3868
|
+
if (docsNavbar?.links && legacyNavbar?.links) {
|
|
3869
|
+
warnings.push({ code: "unsupported-config", message: "Both docs.json navbar.links and mint.json topbarLinks are set; the docs.json navbar.links were used and topbarLinks ignored." });
|
|
3870
|
+
}
|
|
3871
|
+
if (docsNavbar?.primary && legacyNavbar?.primary) {
|
|
3872
|
+
warnings.push({ code: "unsupported-config", message: "Both docs.json navbar.primary and mint.json topbarCtaButton are set; navbar.primary was used and topbarCtaButton ignored." });
|
|
3873
|
+
}
|
|
3874
|
+
if (legacyRaw?.primary && !legacyNavbar?.primary) {
|
|
3875
|
+
warnings.push({ code: "unsupported-config", message: "mint.json topbarCtaButton has no valid url and was skipped." });
|
|
3876
|
+
}
|
|
3877
|
+
const navbar = docsNavbar || legacyNavbar ? {
|
|
3878
|
+
...docsNavbar?.links ?? legacyNavbar?.links ? { links: docsNavbar?.links ?? legacyNavbar?.links } : {},
|
|
3879
|
+
...docsNavbar?.primary ?? legacyNavbar?.primary ? { primary: docsNavbar?.primary ?? legacyNavbar?.primary } : {}
|
|
3880
|
+
} : void 0;
|
|
3881
|
+
const globalLinks = projectedGlobalNavigationLinks(objectValue2(config.navigation)?.global);
|
|
3726
3882
|
const navbarLinks = [...new Map(
|
|
3727
3883
|
[...navbar?.links ?? [], ...globalLinks].map((link) => [`${link.label}:${link.href}`, link])
|
|
3728
3884
|
).values()];
|
|
@@ -3789,11 +3945,11 @@ function isRedirectPathSafe(rawSource, rawDestination) {
|
|
|
3789
3945
|
return true;
|
|
3790
3946
|
}
|
|
3791
3947
|
function mintlifyDefaultVersionPrefixes(config) {
|
|
3792
|
-
const navigation =
|
|
3948
|
+
const navigation = objectValue2(config.navigation) ?? config;
|
|
3793
3949
|
const prefixes = /* @__PURE__ */ new Set();
|
|
3794
3950
|
function collect(container) {
|
|
3795
3951
|
if (Array.isArray(container.versions)) {
|
|
3796
|
-
const entries = container.versions.map((value) =>
|
|
3952
|
+
const entries = container.versions.map((value) => objectValue2(value)).filter((value) => value !== null);
|
|
3797
3953
|
const defaultEntry = entries.find((entry) => entry.default === true) ?? entries[0];
|
|
3798
3954
|
if (defaultEntry && typeof defaultEntry.version === "string" && defaultEntry.version.trim()) {
|
|
3799
3955
|
prefixes.add(defaultEntry.version.trim());
|
|
@@ -3801,7 +3957,7 @@ function mintlifyDefaultVersionPrefixes(config) {
|
|
|
3801
3957
|
}
|
|
3802
3958
|
if (Array.isArray(container.languages)) {
|
|
3803
3959
|
for (const value of container.languages) {
|
|
3804
|
-
const language =
|
|
3960
|
+
const language = objectValue2(value);
|
|
3805
3961
|
if (language) collect(language);
|
|
3806
3962
|
}
|
|
3807
3963
|
}
|
|
@@ -3810,18 +3966,18 @@ function mintlifyDefaultVersionPrefixes(config) {
|
|
|
3810
3966
|
return prefixes;
|
|
3811
3967
|
}
|
|
3812
3968
|
function mintlifyAllVersionPrefixes(config) {
|
|
3813
|
-
const navigation =
|
|
3969
|
+
const navigation = objectValue2(config.navigation) ?? config;
|
|
3814
3970
|
const prefixes = /* @__PURE__ */ new Set();
|
|
3815
3971
|
function collect(container) {
|
|
3816
3972
|
if (Array.isArray(container.versions)) {
|
|
3817
3973
|
for (const value of container.versions) {
|
|
3818
|
-
const entry =
|
|
3974
|
+
const entry = objectValue2(value);
|
|
3819
3975
|
if (entry && typeof entry.version === "string" && entry.version.trim()) prefixes.add(entry.version.trim());
|
|
3820
3976
|
}
|
|
3821
3977
|
}
|
|
3822
3978
|
if (Array.isArray(container.languages)) {
|
|
3823
3979
|
for (const value of container.languages) {
|
|
3824
|
-
const language =
|
|
3980
|
+
const language = objectValue2(value);
|
|
3825
3981
|
if (language) collect(language);
|
|
3826
3982
|
}
|
|
3827
3983
|
}
|
|
@@ -3834,9 +3990,10 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3834
3990
|
const references = [];
|
|
3835
3991
|
const seenReferences = /* @__PURE__ */ new Set();
|
|
3836
3992
|
const warningKeys = /* @__PURE__ */ new Set();
|
|
3837
|
-
const
|
|
3993
|
+
const gated = [];
|
|
3994
|
+
const navigation = objectValue2(config.navigation) ?? config;
|
|
3838
3995
|
const languages = Array.isArray(navigation.languages) ? navigation.languages.flatMap((value) => {
|
|
3839
|
-
const language =
|
|
3996
|
+
const language = objectValue2(value);
|
|
3840
3997
|
if (!language) return [];
|
|
3841
3998
|
try {
|
|
3842
3999
|
const code = Intl.getCanonicalLocales(String(language.language ?? language.locale ?? "en"))[0];
|
|
@@ -3873,7 +4030,8 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3873
4030
|
references,
|
|
3874
4031
|
seenReferences,
|
|
3875
4032
|
warnings,
|
|
3876
|
-
warningKeys
|
|
4033
|
+
warningKeys,
|
|
4034
|
+
gated
|
|
3877
4035
|
};
|
|
3878
4036
|
const languageTabs = convertContainerToTabs(
|
|
3879
4037
|
language,
|
|
@@ -3890,7 +4048,7 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3890
4048
|
}
|
|
3891
4049
|
if (Object.keys(localizedNavigation).length > 0) i18n.navigation = localizedNavigation;
|
|
3892
4050
|
} else {
|
|
3893
|
-
const context = { references, seenReferences, warnings, warningKeys, pathPrefix: options.pathPrefix };
|
|
4051
|
+
const context = { references, seenReferences, warnings, warningKeys, gated, pathPrefix: options.pathPrefix };
|
|
3894
4052
|
tabs = convertContainerToTabs(navigation, context, "Documentation", projectionTrace);
|
|
3895
4053
|
if (tabs.length === 0 && Array.isArray(config.navigation)) {
|
|
3896
4054
|
const children = convertNavigationValues(config.navigation, context);
|
|
@@ -3910,7 +4068,7 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3910
4068
|
});
|
|
3911
4069
|
}
|
|
3912
4070
|
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
3913
|
-
const redirect =
|
|
4071
|
+
const redirect = objectValue2(value);
|
|
3914
4072
|
if (!redirect || typeof redirect.source !== "string" || typeof redirect.destination !== "string") return [];
|
|
3915
4073
|
const rawSource = redirect.source.trim();
|
|
3916
4074
|
const rawDestination = redirect.destination.trim();
|
|
@@ -3929,15 +4087,17 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3929
4087
|
...typeof redirect.permanent === "boolean" ? { permanent: redirect.permanent } : {}
|
|
3930
4088
|
}];
|
|
3931
4089
|
}) : [];
|
|
4090
|
+
collectGatedReferences(navigation, void 0, options.pathPrefix, gated);
|
|
3932
4091
|
return {
|
|
3933
4092
|
docsConfig: {
|
|
3934
4093
|
tabs,
|
|
3935
4094
|
...projectionTrace.rootContainerKind === "dropdowns" ? { navigation: { display: "dropdown" } } : {},
|
|
3936
|
-
...projectedCompatibleConfig(config),
|
|
4095
|
+
...projectedCompatibleConfig(config, warnings),
|
|
3937
4096
|
...i18n ? { i18n } : {},
|
|
3938
4097
|
...redirects.length > 0 ? { redirects } : {}
|
|
3939
4098
|
},
|
|
3940
4099
|
pageReferences: references,
|
|
4100
|
+
gatedReferences: gated,
|
|
3941
4101
|
warnings
|
|
3942
4102
|
};
|
|
3943
4103
|
}
|
|
@@ -4043,7 +4203,7 @@ function buildNavigationFromPages(pages, options = {}) {
|
|
|
4043
4203
|
return { tabs: [{ tab: "Documentation", groups }] };
|
|
4044
4204
|
}
|
|
4045
4205
|
function isDocumentationExtension(filename) {
|
|
4046
|
-
return [".md", ".mdx", ".rst", ".txt"].includes(
|
|
4206
|
+
return [".md", ".mdx", ".rst", ".txt"].includes(extname3(filename).toLowerCase());
|
|
4047
4207
|
}
|
|
4048
4208
|
function pruneMissingNavigationPages(config, availableIds) {
|
|
4049
4209
|
const pruneNodes = (nodes) => {
|
|
@@ -4083,7 +4243,7 @@ var SIDEBAR_FILENAMES = [
|
|
|
4083
4243
|
"sidebars.mjs",
|
|
4084
4244
|
"sidebars.ts"
|
|
4085
4245
|
];
|
|
4086
|
-
function
|
|
4246
|
+
function objectValue3(value) {
|
|
4087
4247
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4088
4248
|
}
|
|
4089
4249
|
function stripNumberPrefix(value) {
|
|
@@ -4405,7 +4565,7 @@ function parseStaticSidebarModule(source) {
|
|
|
4405
4565
|
literal
|
|
4406
4566
|
);
|
|
4407
4567
|
const parsed = JSON5.parse(substituted);
|
|
4408
|
-
const object =
|
|
4568
|
+
const object = objectValue3(parsed);
|
|
4409
4569
|
if (!object) continue;
|
|
4410
4570
|
const bindingName = candidate[0].match(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/)?.[1];
|
|
4411
4571
|
if (bindingName) {
|
|
@@ -4510,7 +4670,7 @@ function readDocusaurusSidebars(repositoryRoot, versionedSidebarPath) {
|
|
|
4510
4670
|
if (!sourcePath) return null;
|
|
4511
4671
|
const absolutePath = resolveWithin(repositoryRoot, sourcePath);
|
|
4512
4672
|
const source = readBoundedText(absolutePath);
|
|
4513
|
-
const parsed =
|
|
4673
|
+
const parsed = extname4(sourcePath).toLowerCase() === ".json" ? objectValue3(JSON5.parse(source)) : parseStaticSidebarModule(source);
|
|
4514
4674
|
if (!parsed) throw new Error("Docusaurus sidebar config must export an object.");
|
|
4515
4675
|
return { config: parsed, sourcePath };
|
|
4516
4676
|
}
|
|
@@ -4536,7 +4696,7 @@ function redirectEntriesFromObjectLiteralText(objectLiteral) {
|
|
|
4536
4696
|
if (!Array.isArray(parsed)) return [];
|
|
4537
4697
|
const entries = [];
|
|
4538
4698
|
for (const raw of parsed) {
|
|
4539
|
-
const entry =
|
|
4699
|
+
const entry = objectValue3(raw);
|
|
4540
4700
|
if (!entry || typeof entry.to !== "string") continue;
|
|
4541
4701
|
const froms = Array.isArray(entry.from) ? entry.from : [entry.from];
|
|
4542
4702
|
for (const from of froms) {
|
|
@@ -4588,7 +4748,7 @@ function readCategoryMetadata(contentRoot, directory) {
|
|
|
4588
4748
|
try {
|
|
4589
4749
|
const raw = readBoundedText(path);
|
|
4590
4750
|
const parsed = filename.endsWith(".json") ? JSON5.parse(raw) : parseYaml2(raw);
|
|
4591
|
-
const object =
|
|
4751
|
+
const object = objectValue3(parsed);
|
|
4592
4752
|
if (!object) return {};
|
|
4593
4753
|
return {
|
|
4594
4754
|
...typeof object.label === "string" ? { label: object.label } : {},
|
|
@@ -4622,7 +4782,7 @@ function registerDoc(docId, context) {
|
|
|
4622
4782
|
return descriptor.navigationId;
|
|
4623
4783
|
}
|
|
4624
4784
|
function generatedIndexPage(label, linkValue, context) {
|
|
4625
|
-
const link =
|
|
4785
|
+
const link = objectValue3(linkValue);
|
|
4626
4786
|
if (!link) return null;
|
|
4627
4787
|
if (link.type === "doc" && typeof link.id === "string") return registerDoc(link.id, context);
|
|
4628
4788
|
if (link.type !== "generated-index") return null;
|
|
@@ -4889,7 +5049,7 @@ function convertItems(value, context) {
|
|
|
4889
5049
|
return page ? [page] : [];
|
|
4890
5050
|
}
|
|
4891
5051
|
if (Array.isArray(value)) return value.flatMap((entry) => convertItems(entry, context));
|
|
4892
|
-
const object =
|
|
5052
|
+
const object = objectValue3(value);
|
|
4893
5053
|
if (!object) return [];
|
|
4894
5054
|
if (object.type === "doc" || object.type === "ref") {
|
|
4895
5055
|
const page = typeof object.id === "string" ? registerDoc(object.id, context) : null;
|
|
@@ -4955,7 +5115,7 @@ function projectDocusaurusNavigation(input) {
|
|
|
4955
5115
|
|
|
4956
5116
|
// src/fern.ts
|
|
4957
5117
|
import { existsSync as existsSync4, lstatSync as lstatSync4, readFileSync as readFileSync4, readdirSync } from "fs";
|
|
4958
|
-
import { basename as basename2, dirname as dirname3, extname as
|
|
5118
|
+
import { basename as basename2, dirname as dirname3, extname as extname5, relative as relative4 } from "path";
|
|
4959
5119
|
import { load } from "cheerio";
|
|
4960
5120
|
import { parse as parseYaml3 } from "yaml";
|
|
4961
5121
|
var MAX_CONFIG_BYTES2 = 2e7;
|
|
@@ -4981,11 +5141,11 @@ function announcementMarkdown(message) {
|
|
|
4981
5141
|
links.set(marker, destination && label ? `[${escapeText(label)}](${destination})` : escapeText(label));
|
|
4982
5142
|
document(element).replaceWith(marker);
|
|
4983
5143
|
});
|
|
4984
|
-
let
|
|
4985
|
-
for (const [marker, link] of links)
|
|
4986
|
-
return
|
|
5144
|
+
let text2 = escapeText(document("body").text());
|
|
5145
|
+
for (const [marker, link] of links) text2 = text2.replaceAll(marker, link);
|
|
5146
|
+
return text2.trim();
|
|
4987
5147
|
}
|
|
4988
|
-
function
|
|
5148
|
+
function objectValue4(value) {
|
|
4989
5149
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4990
5150
|
}
|
|
4991
5151
|
function titleCase3(value) {
|
|
@@ -5001,7 +5161,7 @@ function readFernConfig(fernRoot) {
|
|
|
5001
5161
|
const sourcePath = "docs.yml";
|
|
5002
5162
|
const path = resolveWithin(fernRoot, sourcePath);
|
|
5003
5163
|
if (!existsSync4(path) || !lstatSync4(path).isFile()) return null;
|
|
5004
|
-
const config =
|
|
5164
|
+
const config = objectValue4(readBoundedYaml(path));
|
|
5005
5165
|
if (!config) throw new Error("fern/docs.yml must contain an object.");
|
|
5006
5166
|
return { config, sourcePath };
|
|
5007
5167
|
}
|
|
@@ -5108,8 +5268,8 @@ function registerFolder(object, parentSegments, context) {
|
|
|
5108
5268
|
if (entry.isDirectory()) {
|
|
5109
5269
|
const nested = visit(path, [...routeSegments, fernBasicSlug(entry.name)], depth + 1);
|
|
5110
5270
|
if (nested.length) pages2.push({ group: titleCase3(entry.name), pages: nested });
|
|
5111
|
-
} else if (entry.isFile() && [".md", ".mdx"].includes(
|
|
5112
|
-
const stem = basename2(entry.name,
|
|
5271
|
+
} else if (entry.isFile() && [".md", ".mdx"].includes(extname5(entry.name).toLowerCase())) {
|
|
5272
|
+
const stem = basename2(entry.name, extname5(entry.name));
|
|
5113
5273
|
const pageSegment = stem.toLowerCase() === "index" ? null : fernBasicSlug(stem);
|
|
5114
5274
|
const route = [...routeSegments, ...pageSegment ? [pageSegment] : []].join("/");
|
|
5115
5275
|
const source = lstatSync4(path).size <= MAX_CONFIG_BYTES2 ? readFileSync4(path, "utf8") : "";
|
|
@@ -5134,7 +5294,7 @@ function registerPage(object, parentSegments, context) {
|
|
|
5134
5294
|
return registerPageAt(object.path, base, context, label, object.hidden === true) || null;
|
|
5135
5295
|
}
|
|
5136
5296
|
function convertNode(node, parentSegments, context) {
|
|
5137
|
-
const object =
|
|
5297
|
+
const object = objectValue4(node);
|
|
5138
5298
|
if (!object) return null;
|
|
5139
5299
|
if (typeof object.page === "string") {
|
|
5140
5300
|
const page = registerPage(object, parentSegments, context);
|
|
@@ -5222,7 +5382,7 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
5222
5382
|
}
|
|
5223
5383
|
if (!Array.isArray(config.versions)) return config;
|
|
5224
5384
|
const versions = config.versions.flatMap((entry) => {
|
|
5225
|
-
const version =
|
|
5385
|
+
const version = objectValue4(entry);
|
|
5226
5386
|
return version ? [version] : [];
|
|
5227
5387
|
});
|
|
5228
5388
|
const chosen = versions.find((version) => version.default === true) ?? versions[0];
|
|
@@ -5241,7 +5401,7 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
5241
5401
|
warnOnce2(context, "fern-version-not-file", `Fern version file "${chosen.path}" is not a regular file and was skipped.`);
|
|
5242
5402
|
return config;
|
|
5243
5403
|
}
|
|
5244
|
-
const versionConfig =
|
|
5404
|
+
const versionConfig = objectValue4(readBoundedYaml(versionPath));
|
|
5245
5405
|
if (!versionConfig) return config;
|
|
5246
5406
|
context.pathPrefix = relative4(fernRoot, dirname3(versionPath)).replace(/\\/g, "/");
|
|
5247
5407
|
return { ...config, navigation: versionConfig.navigation, tabs: versionConfig.tabs ?? config.tabs };
|
|
@@ -5256,13 +5416,13 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
5256
5416
|
}
|
|
5257
5417
|
function buildTabsFromConfig(rawConfig, configDir, fernRoot, routePrefix, fallbackTabLabel, context) {
|
|
5258
5418
|
const config = resolveVersionedNavigation(rawConfig, configDir, fernRoot, context);
|
|
5259
|
-
const tabsMeta =
|
|
5419
|
+
const tabsMeta = objectValue4(config.tabs) ?? {};
|
|
5260
5420
|
const navigation = config.navigation;
|
|
5261
|
-
const isTabbedNavigation = Array.isArray(navigation) && navigation.length > 0 && navigation.every((entry) => typeof
|
|
5421
|
+
const isTabbedNavigation = Array.isArray(navigation) && navigation.length > 0 && navigation.every((entry) => typeof objectValue4(entry)?.tab === "string");
|
|
5262
5422
|
if (isTabbedNavigation) {
|
|
5263
5423
|
return navigation.flatMap((entry) => {
|
|
5264
5424
|
const id = String(entry.tab);
|
|
5265
|
-
const meta =
|
|
5425
|
+
const meta = objectValue4(tabsMeta[id]) ?? {};
|
|
5266
5426
|
const label = typeof meta["display-name"] === "string" ? meta["display-name"] : titleCase3(id);
|
|
5267
5427
|
const tabSegment = segmentFor(meta, label, context);
|
|
5268
5428
|
const segments = [...routePrefix, ...tabSegment ? [tabSegment] : []];
|
|
@@ -5284,7 +5444,7 @@ function buildTabsFromConfig(rawConfig, configDir, fernRoot, routePrefix, fallba
|
|
|
5284
5444
|
const index = visiblePages.find((page) => {
|
|
5285
5445
|
const descriptor = descriptors.get(page);
|
|
5286
5446
|
if (!descriptor) return false;
|
|
5287
|
-
const stem = basename2(descriptor.sourcePath,
|
|
5447
|
+
const stem = basename2(descriptor.sourcePath, extname5(descriptor.sourcePath));
|
|
5288
5448
|
return page === indexRoute || stem === "overview" || stem === "index";
|
|
5289
5449
|
});
|
|
5290
5450
|
const entries = visiblePages.filter((page) => page !== index).sort((left, right) => {
|
|
@@ -5333,7 +5493,7 @@ function buildTabsFromConfig(rawConfig, configDir, fernRoot, routePrefix, fallba
|
|
|
5333
5493
|
}
|
|
5334
5494
|
function projectFernProducts(rawProducts, fernRoot, context) {
|
|
5335
5495
|
return rawProducts.flatMap((entry) => {
|
|
5336
|
-
const product =
|
|
5496
|
+
const product = objectValue4(entry);
|
|
5337
5497
|
const rawPath = typeof product?.path === "string" ? product.path.trim() : "";
|
|
5338
5498
|
if (!product || !rawPath) return [];
|
|
5339
5499
|
const label = typeof product["display-name"] === "string" && product["display-name"].trim() ? product["display-name"].trim() : "Product";
|
|
@@ -5350,7 +5510,7 @@ function projectFernProducts(rawProducts, fernRoot, context) {
|
|
|
5350
5510
|
}
|
|
5351
5511
|
let productConfig;
|
|
5352
5512
|
try {
|
|
5353
|
-
productConfig =
|
|
5513
|
+
productConfig = objectValue4(readBoundedYaml(productPath));
|
|
5354
5514
|
} catch (error) {
|
|
5355
5515
|
warnOnce2(
|
|
5356
5516
|
context,
|
|
@@ -5404,7 +5564,7 @@ function projectFernNavigation(input) {
|
|
|
5404
5564
|
});
|
|
5405
5565
|
}
|
|
5406
5566
|
const navbarLinks = Array.isArray(config["navbar-links"]) ? config["navbar-links"].flatMap((entry) => {
|
|
5407
|
-
const link =
|
|
5567
|
+
const link = objectValue4(entry);
|
|
5408
5568
|
const href = typeof link?.url === "string" ? link.url : typeof link?.href === "string" ? link.href : typeof link?.value === "string" ? link.value : void 0;
|
|
5409
5569
|
const label = typeof link?.text === "string" ? link.text : typeof link?.label === "string" ? link.label : link?.type === "github" ? "GitHub" : void 0;
|
|
5410
5570
|
return href && label ? [{ label, href, ...link?.type === "github" ? { type: "github" } : {} }] : [];
|
|
@@ -5414,11 +5574,11 @@ function projectFernNavigation(input) {
|
|
|
5414
5574
|
navbarLinks.push(link);
|
|
5415
5575
|
}
|
|
5416
5576
|
}
|
|
5417
|
-
const announcement =
|
|
5577
|
+
const announcement = objectValue4(config.announcement);
|
|
5418
5578
|
const announcementMessage = typeof announcement?.message === "string" ? announcement.message.trim() : "";
|
|
5419
5579
|
const bannerContent = announcementMarkdown(announcementMessage);
|
|
5420
5580
|
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
5421
|
-
const redirect =
|
|
5581
|
+
const redirect = objectValue4(value);
|
|
5422
5582
|
if (!redirect || typeof redirect.source !== "string" || typeof redirect.destination !== "string") return [];
|
|
5423
5583
|
const rawSource = redirect.source.trim();
|
|
5424
5584
|
const rawDestination = redirect.destination.trim();
|
|
@@ -5501,9 +5661,185 @@ function projectFernNavigation(input) {
|
|
|
5501
5661
|
};
|
|
5502
5662
|
}
|
|
5503
5663
|
|
|
5664
|
+
// src/analytics.ts
|
|
5665
|
+
var GA4_ID = /^G-[A-Z0-9]{4,20}$/;
|
|
5666
|
+
var GTM_ID = /^GTM-[A-Z0-9]{4,12}$/;
|
|
5667
|
+
var POSTHOG_KEY = /^phc_[A-Za-z0-9]{20,64}$/;
|
|
5668
|
+
var HOSTNAME_LABEL = "[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?";
|
|
5669
|
+
var HOSTNAME = new RegExp(`^${HOSTNAME_LABEL}(?:\\.${HOSTNAME_LABEL})*$`);
|
|
5670
|
+
var SAFE_PATH = /^\/[A-Za-z0-9._~\-/]*$/;
|
|
5671
|
+
var MAX_PLAUSIBLE_DOMAINS = 10;
|
|
5672
|
+
var SUPPORTED = ["ga4", "gtm", "posthog", "plausible"];
|
|
5673
|
+
var LEGACY_GA4_ALIAS = "googleAnalytics";
|
|
5674
|
+
function own(record, key) {
|
|
5675
|
+
return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : void 0;
|
|
5676
|
+
}
|
|
5677
|
+
function isRecord(value) {
|
|
5678
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
5679
|
+
}
|
|
5680
|
+
function redact(value) {
|
|
5681
|
+
if (typeof value !== "string") return Array.isArray(value) ? "array" : value === null ? "null" : typeof value;
|
|
5682
|
+
return value.length === 0 ? "empty string" : `"${value.trim().slice(0, 4).replace(/[^\x20-\x7e]/g, "?")}..." (${value.length} chars)`;
|
|
5683
|
+
}
|
|
5684
|
+
function isPublicHostname(hostname) {
|
|
5685
|
+
return !/(^|\.)localhost$/.test(hostname) && !/(^|\.)\d+$/.test(hostname) && !hostname.includes(":") && !hostname.startsWith("[");
|
|
5686
|
+
}
|
|
5687
|
+
function normalizeHttpsUrl(value, allowPath) {
|
|
5688
|
+
if (!/^https:\/\//i.test(value) || /[?#\\]/.test(value)) return null;
|
|
5689
|
+
let url;
|
|
5690
|
+
try {
|
|
5691
|
+
url = new URL(value);
|
|
5692
|
+
} catch {
|
|
5693
|
+
return null;
|
|
5694
|
+
}
|
|
5695
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) return null;
|
|
5696
|
+
if (!HOSTNAME.test(url.hostname) || !isPublicHostname(url.hostname) || url.port === "0") return null;
|
|
5697
|
+
const fullPath = /^https:\/\/[^/]*(\/.*)?$/i.exec(value)?.[1] ?? "";
|
|
5698
|
+
if (fullPath.includes("//")) return null;
|
|
5699
|
+
const path = fullPath.replace(/\/+$/, "");
|
|
5700
|
+
if (path && (!allowPath || !SAFE_PATH.test(path) || path.split("/").some((s) => s === ".." || s === "."))) return null;
|
|
5701
|
+
return `${url.origin}${path}`;
|
|
5702
|
+
}
|
|
5703
|
+
function normalizeDomains(value) {
|
|
5704
|
+
const parts = value.split(",").map((part) => part.trim().toLowerCase());
|
|
5705
|
+
if (parts.length > MAX_PLAUSIBLE_DOMAINS) return null;
|
|
5706
|
+
return parts.every((part) => part.length <= 253 && HOSTNAME.test(part)) ? parts.join(",") : null;
|
|
5707
|
+
}
|
|
5708
|
+
function normalizeServer(value) {
|
|
5709
|
+
if (/^https:\/\//i.test(value)) {
|
|
5710
|
+
const origin2 = normalizeHttpsUrl(value, false);
|
|
5711
|
+
return origin2 ? origin2.replace(/^https:\/\//, "") : null;
|
|
5712
|
+
}
|
|
5713
|
+
const bare = /^([a-z0-9.-]+)(?::(\d{1,5}))?$/i.exec(value);
|
|
5714
|
+
if (!bare || bare[2] !== void 0 && (Number(bare[2]) < 1 || Number(bare[2]) > 65535)) return null;
|
|
5715
|
+
const origin = normalizeHttpsUrl(`https://${bare[1].toLowerCase()}${bare[2] === void 0 ? "" : `:${Number(bare[2])}`}`, false);
|
|
5716
|
+
return origin ? origin.replace(/^https:\/\//, "") : null;
|
|
5717
|
+
}
|
|
5718
|
+
function text(value) {
|
|
5719
|
+
return typeof value === "string" && value.trim() && value.length <= 2048 ? value.trim() : null;
|
|
5720
|
+
}
|
|
5721
|
+
function projectMintlifyIntegrations(config) {
|
|
5722
|
+
if (!isRecord(config)) return { warnings: [] };
|
|
5723
|
+
try {
|
|
5724
|
+
return project(config);
|
|
5725
|
+
} catch {
|
|
5726
|
+
return { warnings: [{ code: "unsupported-config", message: "Mintlify analytics configuration could not be read; analytics were not imported." }] };
|
|
5727
|
+
}
|
|
5728
|
+
}
|
|
5729
|
+
function project(config) {
|
|
5730
|
+
const warnings = [];
|
|
5731
|
+
const warn = (message) => warnings.push({ code: "unsupported-config", message });
|
|
5732
|
+
const sources = [];
|
|
5733
|
+
for (const [label, key] of [["integrations", "integrations"], ["analytics", "analytics"]]) {
|
|
5734
|
+
const value = own(config, key);
|
|
5735
|
+
if (value === void 0) continue;
|
|
5736
|
+
if (isRecord(value)) sources.push({ label, block: value });
|
|
5737
|
+
else warn(`Mintlify ${label} must be an object (found ${redact(value)}); analytics from it were not imported.`);
|
|
5738
|
+
}
|
|
5739
|
+
const integrations = {};
|
|
5740
|
+
const pick = (name) => {
|
|
5741
|
+
const found = [];
|
|
5742
|
+
for (const source of sources) {
|
|
5743
|
+
let value = own(source.block, name);
|
|
5744
|
+
if (value === void 0 && name === "ga4" && source.label === "analytics") value = own(source.block, LEGACY_GA4_ALIAS);
|
|
5745
|
+
if (value !== void 0) found.push({ label: `${source.label}.${name}`, value });
|
|
5746
|
+
}
|
|
5747
|
+
if (found.length > 1) {
|
|
5748
|
+
warn(`Mintlify ${found[0].label} and ${found[1].label} are both set; using ${found[0].label} for ${name}.`);
|
|
5749
|
+
}
|
|
5750
|
+
return found;
|
|
5751
|
+
};
|
|
5752
|
+
const field = (label, block, key, required) => {
|
|
5753
|
+
const raw = own(block, key);
|
|
5754
|
+
if (raw === void 0) {
|
|
5755
|
+
if (required) warn(`Mintlify ${label}.${key} is missing; ${label} was not imported.`);
|
|
5756
|
+
return required ? null : void 0;
|
|
5757
|
+
}
|
|
5758
|
+
const value = text(raw);
|
|
5759
|
+
if (value === null) warn(`Mintlify ${label}.${key} is not a usable string (found ${redact(raw)}); ${label} was not imported.`);
|
|
5760
|
+
return value;
|
|
5761
|
+
};
|
|
5762
|
+
const objectFor = (entry) => {
|
|
5763
|
+
if (isRecord(entry.value)) return entry.value;
|
|
5764
|
+
warn(`Mintlify ${entry.label} must be an object (found ${redact(entry.value)}); provider was not imported.`);
|
|
5765
|
+
return null;
|
|
5766
|
+
};
|
|
5767
|
+
const ga4 = pick("ga4")[0];
|
|
5768
|
+
const ga4Block = ga4 ? objectFor(ga4) : null;
|
|
5769
|
+
if (ga4 && ga4Block) {
|
|
5770
|
+
const id = field(ga4.label, ga4Block, "measurementId", true);
|
|
5771
|
+
if (id && GA4_ID.test(id)) integrations.ga4 = { measurementId: id };
|
|
5772
|
+
else if (id) warn(`Mintlify ${ga4.label}.measurementId ${redact(id)} is not a GA4 ID (G-XXXXXXXXXX); Universal Analytics and lowercase IDs are not imported.`);
|
|
5773
|
+
}
|
|
5774
|
+
const gtm = pick("gtm")[0];
|
|
5775
|
+
const gtmBlock = gtm ? objectFor(gtm) : null;
|
|
5776
|
+
if (gtm && gtmBlock) {
|
|
5777
|
+
const id = field(gtm.label, gtmBlock, "tagId", true);
|
|
5778
|
+
if (id && GTM_ID.test(id)) integrations.gtm = { tagId: id };
|
|
5779
|
+
else if (id) warn(`Mintlify ${gtm.label}.tagId ${redact(id)} is not a GTM container ID (GTM-XXXXXXX); GTM was not imported.`);
|
|
5780
|
+
}
|
|
5781
|
+
const plausible = pick("plausible")[0];
|
|
5782
|
+
const plausibleBlock = plausible ? objectFor(plausible) : null;
|
|
5783
|
+
if (plausible && plausibleBlock) {
|
|
5784
|
+
const domainRaw = field(plausible.label, plausibleBlock, "domain", true);
|
|
5785
|
+
const serverRaw = field(plausible.label, plausibleBlock, "server", false);
|
|
5786
|
+
const domain = domainRaw ? normalizeDomains(domainRaw) : null;
|
|
5787
|
+
const server = serverRaw ? normalizeServer(serverRaw) : serverRaw;
|
|
5788
|
+
if (domainRaw && !domain) warn(`Mintlify ${plausible.label}.domain ${redact(domainRaw)} must be hostname(s) without a protocol or path; Plausible was not imported.`);
|
|
5789
|
+
if (serverRaw && !server) warn(`Mintlify ${plausible.label}.server ${redact(serverRaw)} must be a hostname; Plausible was not imported.`);
|
|
5790
|
+
if (domain && server !== null) integrations.plausible = { domain, ...server ? { server } : {} };
|
|
5791
|
+
}
|
|
5792
|
+
const posthog = pick("posthog")[0];
|
|
5793
|
+
const posthogBlock = posthog ? objectFor(posthog) : null;
|
|
5794
|
+
if (posthog && posthogBlock) {
|
|
5795
|
+
const key = field(posthog.label, posthogBlock, "apiKey", true);
|
|
5796
|
+
const hostRaw = field(posthog.label, posthogBlock, "apiHost", false);
|
|
5797
|
+
const host = hostRaw ? normalizeHttpsUrl(hostRaw, true) : hostRaw;
|
|
5798
|
+
const recording = own(posthogBlock, "sessionRecording");
|
|
5799
|
+
if (recording !== void 0 && typeof recording !== "boolean") warn(`Mintlify ${posthog.label}.sessionRecording must be a boolean (found ${redact(recording)}); session recording disabled.`);
|
|
5800
|
+
if (key && !POSTHOG_KEY.test(key)) warn(`Mintlify ${posthog.label}.apiKey ${redact(key)} is not a PostHog project key (phc_...); PostHog was not imported.`);
|
|
5801
|
+
if (hostRaw && !host) warn(`Mintlify ${posthog.label}.apiHost ${redact(hostRaw)} must be an https URL without credentials, query, or fragment; PostHog was not imported.`);
|
|
5802
|
+
if (key && POSTHOG_KEY.test(key) && host !== null) {
|
|
5803
|
+
integrations.posthog = {
|
|
5804
|
+
apiKey: key,
|
|
5805
|
+
...host ? { apiHost: host } : {},
|
|
5806
|
+
// Fail closed: a present non-boolean value turns recording off.
|
|
5807
|
+
...recording !== void 0 ? { sessionRecording: recording === true } : {}
|
|
5808
|
+
};
|
|
5809
|
+
}
|
|
5810
|
+
}
|
|
5811
|
+
const unsupported = /* @__PURE__ */ new Set();
|
|
5812
|
+
for (const source of sources) {
|
|
5813
|
+
for (const key of Object.keys(source.block)) {
|
|
5814
|
+
if (SUPPORTED.includes(key)) continue;
|
|
5815
|
+
if (key === LEGACY_GA4_ALIAS && source.label === "analytics") continue;
|
|
5816
|
+
unsupported.add(/^[A-Za-z0-9_-]{1,40}$/.test(key) ? key : "(invalid name)");
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
5819
|
+
if (unsupported.size > 0) {
|
|
5820
|
+
const names = [...unsupported].slice(0, 30);
|
|
5821
|
+
warn(`Mintlify analytics/integration providers Thally cannot render were not imported: ${names.join(", ")}${unsupported.size > names.length ? ", ..." : ""}. Supported: ga4, gtm, posthog, plausible.`);
|
|
5822
|
+
}
|
|
5823
|
+
return { ...Object.keys(integrations).length > 0 ? { integrations } : {}, warnings };
|
|
5824
|
+
}
|
|
5825
|
+
function validateIntegrations(integrations) {
|
|
5826
|
+
if (integrations === void 0) return [];
|
|
5827
|
+
const result = projectMintlifyIntegrations({ integrations });
|
|
5828
|
+
const messages = result.warnings.map(
|
|
5829
|
+
(warning) => warning.message.replace(/^Mintlify analytics\/integration providers Thally cannot render were not imported: /, "docs.json integrations: Thally does not support these providers, so they will not be rendered: ").replace(/^Mintlify /, "docs.json ").replace(/\b(?:was|were|are) not imported\b/g, "will not be rendered")
|
|
5830
|
+
);
|
|
5831
|
+
if (result.integrations?.ga4 && result.integrations.gtm) {
|
|
5832
|
+
messages.push("docs.json integrations enable both ga4 and gtm; a GA4 tag inside the GTM container would double-count page views.");
|
|
5833
|
+
}
|
|
5834
|
+
return messages;
|
|
5835
|
+
}
|
|
5836
|
+
|
|
5504
5837
|
// src/repository.ts
|
|
5505
5838
|
var MAX_SOURCE_FILES = 5e3;
|
|
5506
5839
|
var MAX_PAGE_BYTES = 2e6;
|
|
5840
|
+
var FRONTMATTER_HEAD_BYTES = 65536;
|
|
5841
|
+
var MAX_WITHHELD_SCAN_BYTES = MAX_PAGE_BYTES * 8;
|
|
5842
|
+
var MAX_DROPPED_GATED_WARNINGS = 20;
|
|
5507
5843
|
var MAX_ASSET_BYTES = 25e6;
|
|
5508
5844
|
var MAX_TOTAL_ASSET_BYTES = 5e8;
|
|
5509
5845
|
var GIT_LFS_POINTER_PREFIX = "version https://git-lfs.github.com/spec/v1";
|
|
@@ -5529,6 +5865,7 @@ var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
|
5529
5865
|
function isIgnoredContentDirectory(name) {
|
|
5530
5866
|
return name.startsWith(".") || name === "node_modules";
|
|
5531
5867
|
}
|
|
5868
|
+
var QUARANTINE_DIRECTORY = "migration-quarantine";
|
|
5532
5869
|
var ASSET_DIRECTORIES = /* @__PURE__ */ new Set(["assets", "images", "img", "media", "public", "static"]);
|
|
5533
5870
|
var ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5534
5871
|
".avif",
|
|
@@ -6144,7 +6481,7 @@ function containsMarkdown(directory, depth = 0) {
|
|
|
6144
6481
|
for (const entry of readdirSync2(directory, { withFileTypes: true })) {
|
|
6145
6482
|
if (isIgnoredDirectory(entry.name) || entry.isSymbolicLink()) continue;
|
|
6146
6483
|
const path = resolveWithin(directory, entry.name);
|
|
6147
|
-
if (entry.isFile() && [".md", ".mdx"].includes(
|
|
6484
|
+
if (entry.isFile() && [".md", ".mdx"].includes(extname6(entry.name).toLowerCase())) return true;
|
|
6148
6485
|
if (entry.isDirectory() && containsMarkdown(path, depth + 1)) return true;
|
|
6149
6486
|
}
|
|
6150
6487
|
return false;
|
|
@@ -6156,10 +6493,10 @@ function detectRepositoryDocsDir(repositoryDir) {
|
|
|
6156
6493
|
}
|
|
6157
6494
|
return "";
|
|
6158
6495
|
}
|
|
6159
|
-
var
|
|
6160
|
-
function scanFiles(root, confinementRoot = root, warnings, rank) {
|
|
6496
|
+
var RANKED_WALK_FACTOR = 20;
|
|
6497
|
+
function scanFiles(root, confinementRoot = root, warnings, rank, budget = MAX_SOURCE_FILES) {
|
|
6161
6498
|
const files = [];
|
|
6162
|
-
const walkCap = rank ?
|
|
6499
|
+
const walkCap = rank ? budget * RANKED_WALK_FACTOR : budget;
|
|
6163
6500
|
let confinementReal;
|
|
6164
6501
|
try {
|
|
6165
6502
|
confinementReal = realpathSync2(confinementRoot);
|
|
@@ -6229,10 +6566,10 @@ function scanFiles(root, confinementRoot = root, warnings, rank) {
|
|
|
6229
6566
|
}
|
|
6230
6567
|
return files;
|
|
6231
6568
|
}
|
|
6232
|
-
function selectGroupWithinBudget(scanned, rank, warnings, allVersionPrefixes, label) {
|
|
6233
|
-
if (scanned.length <=
|
|
6569
|
+
function selectGroupWithinBudget(scanned, rank, warnings, allVersionPrefixes, label, budget = MAX_SOURCE_FILES) {
|
|
6570
|
+
if (scanned.length <= budget) return scanned;
|
|
6234
6571
|
const ranked = scanned.map((file) => ({ file, priority: rank(file.relativePath) })).sort((left, right) => left.priority - right.priority || (left.file.relativePath < right.file.relativePath ? -1 : left.file.relativePath > right.file.relativePath ? 1 : 0));
|
|
6235
|
-
const dropped = ranked.slice(
|
|
6572
|
+
const dropped = ranked.slice(budget).map(({ file }) => file);
|
|
6236
6573
|
if (warnings) {
|
|
6237
6574
|
const droppedPages = label === "file" ? dropped.filter((file) => isDocumentationExtension(file.relativePath)) : dropped;
|
|
6238
6575
|
const noun = label === "file" ? "page" : "image or media file";
|
|
@@ -6245,18 +6582,18 @@ function selectGroupWithinBudget(scanned, rank, warnings, allVersionPrefixes, la
|
|
|
6245
6582
|
const rest = droppedPages.length - examples.length;
|
|
6246
6583
|
warnings.push({
|
|
6247
6584
|
code: "limit-reached",
|
|
6248
|
-
message: `This repository has more than ${
|
|
6585
|
+
message: `This repository has more than ${budget} ${label === "file" ? "files" : "assets"}, so only the first ${budget} (in navigation order, default version first) were migrated. ${droppedPages.length} ${noun}(s) were left out` + (examples.length > 0 ? `: ${examples.join(", ")}${rest > 0 ? `, and ${rest} more` : ""}` : "") + (droppedVersions.size > 0 ? ` (versions: ${[...droppedVersions].slice(0, 5).join(", ")}${droppedVersions.size > 5 ? `, and ${droppedVersions.size - 5} more` : ""})` : "") + (label === "file" ? ". To include them, run the migration on a smaller part of the repository with --docs-dir." : ". Copy them into public/ manually if your pages use them.")
|
|
6249
6586
|
});
|
|
6250
6587
|
}
|
|
6251
|
-
return ranked.slice(0,
|
|
6588
|
+
return ranked.slice(0, budget).map(({ file }) => file);
|
|
6252
6589
|
}
|
|
6253
|
-
function selectFilesWithinBudget(scanned, rank, warnings, allVersionPrefixes) {
|
|
6254
|
-
const isAsset = (file) => ASSET_EXTENSIONS.has(
|
|
6590
|
+
function selectFilesWithinBudget(scanned, rank, warnings, allVersionPrefixes, budget = MAX_SOURCE_FILES) {
|
|
6591
|
+
const isAsset = (file) => ASSET_EXTENSIONS.has(extname6(file.relativePath).toLowerCase());
|
|
6255
6592
|
const assetFiles = scanned.filter(isAsset);
|
|
6256
6593
|
const otherFiles = scanned.filter((file) => !isAsset(file));
|
|
6257
6594
|
return [
|
|
6258
|
-
...selectGroupWithinBudget(otherFiles, rank, warnings, allVersionPrefixes, "file"),
|
|
6259
|
-
...selectGroupWithinBudget(assetFiles, rank, warnings, allVersionPrefixes, "asset")
|
|
6595
|
+
...selectGroupWithinBudget(otherFiles, rank, warnings, allVersionPrefixes, "file", budget),
|
|
6596
|
+
...selectGroupWithinBudget(assetFiles, rank, warnings, allVersionPrefixes, "asset", budget)
|
|
6260
6597
|
];
|
|
6261
6598
|
}
|
|
6262
6599
|
function readMintignoreMatcher(mintlifyRoot) {
|
|
@@ -6301,6 +6638,45 @@ function normalizedReferenceKey(value) {
|
|
|
6301
6638
|
function exactReferenceKey(value) {
|
|
6302
6639
|
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
6303
6640
|
}
|
|
6641
|
+
function specRefMatches(ref, specPath, specFilename, isDefault) {
|
|
6642
|
+
const prefix = /^(?:(\S+)\s+)?(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|TRACE|WEBHOOK)\s/i.exec(`${ref} `)?.[1];
|
|
6643
|
+
if (!prefix) return isDefault;
|
|
6644
|
+
const wanted = prefix.replace(/^\/+/, "").toLowerCase();
|
|
6645
|
+
const lowerPath = specPath.toLowerCase();
|
|
6646
|
+
return lowerPath === wanted || lowerPath.endsWith(`/${wanted}`) || specFilename.toLowerCase() === wanted;
|
|
6647
|
+
}
|
|
6648
|
+
var TOKEN_SEPARATORS = /[\s"'()<>[\]{}=,;:|\\/`*!?#&]+/;
|
|
6649
|
+
var PATH_SEPARATORS = /[\s"'()<>[\]{}=,;:|\\`*!?#&]+/;
|
|
6650
|
+
function publicAssetKey(repoRelative) {
|
|
6651
|
+
return repoRelative.split("/", 1)[0].toLowerCase() === "public" ? repoRelative.slice("public/".length) : repoRelative;
|
|
6652
|
+
}
|
|
6653
|
+
function addPathReferences(text2, baseDir, exact, loose) {
|
|
6654
|
+
for (const word of text2.split(PATH_SEPARATORS)) {
|
|
6655
|
+
let token = word.replace(/\.+$/, "");
|
|
6656
|
+
if (!/\.[a-z0-9]{2,5}$/i.test(token) || token.startsWith("//")) continue;
|
|
6657
|
+
if (token.includes("%")) {
|
|
6658
|
+
try {
|
|
6659
|
+
token = decodeURIComponent(token);
|
|
6660
|
+
} catch {
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
if (!token.includes("/")) {
|
|
6664
|
+
loose.add(token.toLowerCase());
|
|
6665
|
+
continue;
|
|
6666
|
+
}
|
|
6667
|
+
if (!token.startsWith("/") && baseDir === void 0) continue;
|
|
6668
|
+
const resolved = normalizeAssetPath(posix3.normalize(token.startsWith("/") ? token.slice(1) : posix3.join(baseDir ?? "", token)));
|
|
6669
|
+
if (!resolved) continue;
|
|
6670
|
+
const key = publicAssetKey(resolved);
|
|
6671
|
+
exact.add(key);
|
|
6672
|
+
loose.add(key.toLowerCase());
|
|
6673
|
+
}
|
|
6674
|
+
}
|
|
6675
|
+
function looselyNamed(assetPath, loose) {
|
|
6676
|
+
const lower = assetPath.toLowerCase();
|
|
6677
|
+
const name = lower.split("/").pop() ?? lower;
|
|
6678
|
+
return loose.has(lower) || loose.has(name) || loose.has(name.split(TOKEN_SEPARATORS).filter(Boolean).pop() ?? name);
|
|
6679
|
+
}
|
|
6304
6680
|
function findOpenApi(files) {
|
|
6305
6681
|
return files.find((file) => OPENAPI_FILENAMES.has(basename3(file.relativePath).toLowerCase()) && classifyApiSpec(file.absolutePath) === "openapi") ?? null;
|
|
6306
6682
|
}
|
|
@@ -6317,8 +6693,8 @@ function classifyApiSpec(path) {
|
|
|
6317
6693
|
} catch {
|
|
6318
6694
|
return "unknown";
|
|
6319
6695
|
}
|
|
6320
|
-
const
|
|
6321
|
-
const topLevel =
|
|
6696
|
+
const text2 = buffer.toString("utf8", 0, bytesRead);
|
|
6697
|
+
const topLevel = text2.match(/^(?:openapi|swagger|asyncapi)\s*:/m)?.[0] ?? text2.match(/^\s*\{\s*"(?:openapi|swagger|asyncapi)"\s*:/m)?.[0];
|
|
6322
6698
|
if (!topLevel) return "unknown";
|
|
6323
6699
|
return /asyncapi/i.test(topLevel) ? "asyncapi" : "openapi";
|
|
6324
6700
|
}
|
|
@@ -6635,6 +7011,31 @@ function fernDefinitionExists(fernRoot, apiName) {
|
|
|
6635
7011
|
}
|
|
6636
7012
|
return candidateDirs.some((dir) => existsSync5(dir) && lstatSync5(dir).isDirectory());
|
|
6637
7013
|
}
|
|
7014
|
+
function readFrontmatterHead(path) {
|
|
7015
|
+
const fd = openSync(path, "r");
|
|
7016
|
+
try {
|
|
7017
|
+
const buffer = Buffer.alloc(FRONTMATTER_HEAD_BYTES);
|
|
7018
|
+
const head = buffer.toString("utf8", 0, readSync(fd, buffer, 0, buffer.length, 0));
|
|
7019
|
+
const opening = /^\uFEFF?---([^\r\n]*)\r?\n/.exec(head);
|
|
7020
|
+
if (!opening || opening[1].startsWith("-")) return head;
|
|
7021
|
+
return /^---[ \t]*\r?\n/m.test(head.slice(opening[0].length)) ? head : void 0;
|
|
7022
|
+
} finally {
|
|
7023
|
+
closeSync(fd);
|
|
7024
|
+
}
|
|
7025
|
+
}
|
|
7026
|
+
function canonicalPath(path) {
|
|
7027
|
+
try {
|
|
7028
|
+
return realpathSync2.native(path);
|
|
7029
|
+
} catch {
|
|
7030
|
+
return path;
|
|
7031
|
+
}
|
|
7032
|
+
}
|
|
7033
|
+
function pageHeadGateReason(raw) {
|
|
7034
|
+
const parsed = parseFrontmatter(raw);
|
|
7035
|
+
const unreadable = parsed.error && /^(groups|public)\s*:/m.test(raw.slice(0, raw.length - parsed.content.length)) ? "frontmatter could not be parsed and declares `groups` or `public`" : void 0;
|
|
7036
|
+
const openapi = typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0;
|
|
7037
|
+
return { reason: frontmatterGateReason(parsed.data) ?? unreadable, publicTrue: isPublicTrue(parsed.data.public), openapi };
|
|
7038
|
+
}
|
|
6638
7039
|
function withoutFrontmatter(value) {
|
|
6639
7040
|
return value.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
6640
7041
|
}
|
|
@@ -6769,7 +7170,7 @@ function fernSourceSiteUrl(config) {
|
|
|
6769
7170
|
function externalizeMissingFernLinks(body, pageIds, sourceSiteUrl, externalized) {
|
|
6770
7171
|
const rewrite = (target) => {
|
|
6771
7172
|
const path = target.split(/[?#]/, 1)[0].replace(/^\//, "").replace(/\/$/, "");
|
|
6772
|
-
if (!path || pageIds.has(path) || ASSET_EXTENSIONS.has(
|
|
7173
|
+
if (!path || pageIds.has(path) || ASSET_EXTENSIONS.has(extname6(path).toLowerCase())) return target;
|
|
6773
7174
|
try {
|
|
6774
7175
|
const base = new URL(`${sourceSiteUrl}/`);
|
|
6775
7176
|
const sourceLinkUrl = new URL(target.slice(1), base);
|
|
@@ -6781,12 +7182,12 @@ function externalizeMissingFernLinks(body, pageIds, sourceSiteUrl, externalized)
|
|
|
6781
7182
|
return target;
|
|
6782
7183
|
}
|
|
6783
7184
|
};
|
|
6784
|
-
return replaceOutsideCode(body, (
|
|
7185
|
+
return replaceOutsideCode(body, (text2) => text2.replace(/(\]\()\/(?!\/)([^\s)]+)(\))/g, (_match, before, target, after) => `${before}${rewrite(`/${target}`)}${after}`).replace(/(\b(?:href|to)=(['"]))\/(?!\/)([^'"\n]+)\2/g, (_match, before, quote, target) => `${before}${rewrite(`/${target}`)}${quote}`));
|
|
6785
7186
|
}
|
|
6786
7187
|
function stripFernBasePathFromLinks(body, basePath) {
|
|
6787
7188
|
if (!basePath) return body;
|
|
6788
7189
|
const escaped = basePath.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6789
|
-
return replaceOutsideCode(body, (
|
|
7190
|
+
return replaceOutsideCode(body, (text2) => text2.replace(new RegExp(`(\\]\\()${escaped}(?=\\/|[)#?])`, "g"), (match, prefix, offset, source) => prefix + (source[offset + match.length] === "/" ? "" : "/")).replace(new RegExp(`(\\b(?:href|to)=["'])${escaped}(?=\\/|["'#?])`, "g"), (match, prefix, offset, source) => prefix + (source[offset + match.length] === "/" ? "" : "/")));
|
|
6790
7191
|
}
|
|
6791
7192
|
function rewriteMintlifyMountedLinks(body, pageIds) {
|
|
6792
7193
|
const rewrite = (target) => {
|
|
@@ -6803,7 +7204,7 @@ function rewriteMintlifyMountedLinks(body, pageIds) {
|
|
|
6803
7204
|
}
|
|
6804
7205
|
return target;
|
|
6805
7206
|
};
|
|
6806
|
-
return replaceOutsideCode(body, (
|
|
7207
|
+
return replaceOutsideCode(body, (text2) => text2.replace(/(\]\()(\/[^\s)]+)(\))/g, (_match, before, target, after) => `${before}${rewrite(target)}${after}`).replace(/(\b(?:href|to)=(['"]))(\/[^'"\n]+)\2/g, (_match, before, quote, target) => `${before}${rewrite(target)}${quote}`));
|
|
6807
7208
|
}
|
|
6808
7209
|
function preserveLinkedAnchors(pages) {
|
|
6809
7210
|
const pageById = new Map(pages.map((page) => [page.id, page]));
|
|
@@ -6827,10 +7228,10 @@ function preserveLinkedAnchors(pages) {
|
|
|
6827
7228
|
if (id !== currentId) crossPageRequests.add(`${id}#${fragment}`);
|
|
6828
7229
|
};
|
|
6829
7230
|
for (const page of pages) {
|
|
6830
|
-
replaceOutsideCode(page.body, (
|
|
6831
|
-
for (const match of
|
|
6832
|
-
for (const match of
|
|
6833
|
-
return
|
|
7231
|
+
replaceOutsideCode(page.body, (text2) => {
|
|
7232
|
+
for (const match of text2.matchAll(/\]\(([^)\s]+)\)/g)) addTarget(match[1], page.id);
|
|
7233
|
+
for (const match of text2.matchAll(/\b(?:href|to)=(['"])([^'"\n]+)\1/g)) addTarget(match[2], page.id);
|
|
7234
|
+
return text2;
|
|
6834
7235
|
});
|
|
6835
7236
|
}
|
|
6836
7237
|
const comparable = (value) => value.replace(/&(?:lt|gt|amp|quot);/g, " ").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "");
|
|
@@ -6854,9 +7255,9 @@ function preserveLinkedAnchors(pages) {
|
|
|
6854
7255
|
return match ? { index, text: match[1] } : null;
|
|
6855
7256
|
}).filter((entry) => entry !== null);
|
|
6856
7257
|
const existing = /* @__PURE__ */ new Set();
|
|
6857
|
-
replaceOutsideCode(page.body, (
|
|
6858
|
-
for (const match of
|
|
6859
|
-
return
|
|
7258
|
+
replaceOutsideCode(page.body, (text2) => {
|
|
7259
|
+
for (const match of text2.matchAll(/\bid=(?:"([^"]+)"|'([^']+)')/g)) existing.add(match[1] ?? match[2]);
|
|
7260
|
+
return text2;
|
|
6860
7261
|
});
|
|
6861
7262
|
const candidateIndexes = /* @__PURE__ */ new Map();
|
|
6862
7263
|
for (const heading of headings) {
|
|
@@ -6963,8 +7364,13 @@ function globalSnippetAliases(files, repositoryRoot, siteRoot) {
|
|
|
6963
7364
|
if (conventionalAlias) aliases.set(conventionalAlias, file.absolutePath);
|
|
6964
7365
|
}
|
|
6965
7366
|
for (const file of files) {
|
|
6966
|
-
if (![".md", ".mdx"].includes(
|
|
6967
|
-
|
|
7367
|
+
if (![".md", ".mdx"].includes(extname6(file.relativePath).toLowerCase())) continue;
|
|
7368
|
+
let raw;
|
|
7369
|
+
try {
|
|
7370
|
+
raw = readFileSync5(file.absolutePath, "utf8");
|
|
7371
|
+
} catch {
|
|
7372
|
+
continue;
|
|
7373
|
+
}
|
|
6968
7374
|
for (const match of raw.matchAll(SNIPPET_IMPORT_PATTERN)) {
|
|
6969
7375
|
try {
|
|
6970
7376
|
const componentName = match[1] ?? match[2];
|
|
@@ -6991,7 +7397,7 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced) {
|
|
|
6991
7397
|
} catch {
|
|
6992
7398
|
return null;
|
|
6993
7399
|
}
|
|
6994
|
-
if (!ASSET_EXTENSIONS.has(
|
|
7400
|
+
if (!ASSET_EXTENSIONS.has(extname6(decodedPath).toLowerCase())) return null;
|
|
6995
7401
|
try {
|
|
6996
7402
|
const candidate = decodedPath.startsWith("/") ? resolveWithin(siteRoot, decodedPath.replace(/^\/+/, "")) : resolveWithinRoot(dirname4(currentFile), decodedPath, siteRoot);
|
|
6997
7403
|
const realRoot = realpathSync2(siteRoot);
|
|
@@ -7003,7 +7409,9 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced) {
|
|
|
7003
7409
|
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile()) return null;
|
|
7004
7410
|
const normalized = normalizeAssetPath(siteRelative);
|
|
7005
7411
|
if (!normalized) return null;
|
|
7006
|
-
|
|
7412
|
+
const onDiskRelative = relative5(realRoot, realpathSync2.native(candidate)).replace(/\\/g, "/");
|
|
7413
|
+
const onDisk = onDiskRelative.toLowerCase() === siteRelative.toLowerCase() ? normalizeAssetPath(onDiskRelative) : null;
|
|
7414
|
+
onReferenced?.(normalized, onDisk ?? void 0);
|
|
7007
7415
|
const rewritten = `/${normalized}${suffix}`;
|
|
7008
7416
|
return isBracketed ? `<${rewritten}>` : rewritten;
|
|
7009
7417
|
} catch {
|
|
@@ -7011,7 +7419,7 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced) {
|
|
|
7011
7419
|
}
|
|
7012
7420
|
}
|
|
7013
7421
|
function rewriteFernIdRenameLinks(body, renames) {
|
|
7014
|
-
return replaceOutsideCode(body, (
|
|
7422
|
+
return replaceOutsideCode(body, (text2) => {
|
|
7015
7423
|
const rewriteTarget = (target) => {
|
|
7016
7424
|
const suffixIndex = target.search(/[?#]/);
|
|
7017
7425
|
const path = (suffixIndex >= 0 ? target.slice(0, suffixIndex) : target).replace(/^\/+/, "");
|
|
@@ -7019,7 +7427,7 @@ function rewriteFernIdRenameLinks(body, renames) {
|
|
|
7019
7427
|
const renamed = renames.get(path);
|
|
7020
7428
|
return renamed ? `/${renamed}${suffix}` : target;
|
|
7021
7429
|
};
|
|
7022
|
-
return
|
|
7430
|
+
return text2.replace(/(\]\()\/([^\s)]+)(?=[\s)]|$)/g, (_match, prefix, target) => `${prefix}${rewriteTarget(`/${target}`)}`).replace(/(\bhref=")\/([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${rewriteTarget(`/${target}`)}${suffix}`);
|
|
7023
7431
|
});
|
|
7024
7432
|
}
|
|
7025
7433
|
function rewriteRepositoryAssetLinks(body, currentFile, siteRoot, onReferenced) {
|
|
@@ -7048,18 +7456,47 @@ function locallyDeclaredNames(raw) {
|
|
|
7048
7456
|
}
|
|
7049
7457
|
return names;
|
|
7050
7458
|
}
|
|
7051
|
-
function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0, siteRoot = repositoryRoot, globalAliases = /* @__PURE__ */ new Map()) {
|
|
7459
|
+
function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0, siteRoot = repositoryRoot, globalAliases = /* @__PURE__ */ new Map(), gate) {
|
|
7052
7460
|
if (depth >= 8) return raw;
|
|
7053
7461
|
raw = normalizeIndentedFences(raw);
|
|
7054
7462
|
const snippets = /* @__PURE__ */ new Map();
|
|
7055
7463
|
const preservedDeclarations = /* @__PURE__ */ new Map();
|
|
7056
|
-
|
|
7464
|
+
const source = relative5(repositoryRoot, currentFile).replace(/\\/g, "/");
|
|
7465
|
+
const blockInline = (candidate, shownPath, label = shownPath) => {
|
|
7466
|
+
const verdict = gate?.(candidate);
|
|
7467
|
+
if (!verdict) return void 0;
|
|
7468
|
+
const shown = shownPath.replace(/\*\//g, "* /");
|
|
7469
|
+
if (verdict.kind === "gated") {
|
|
7470
|
+
warnings.push({ code: "gated-page", message: `${label} is access-restricted and was NOT inlined; it was left as a comment.`, source });
|
|
7471
|
+
return `{/* Access-restricted content withheld: ${shown} */}`;
|
|
7472
|
+
}
|
|
7473
|
+
if (verdict.kind === "snippet-gated") {
|
|
7474
|
+
warnings.push({
|
|
7475
|
+
code: "gated-page",
|
|
7476
|
+
message: `${label} declares access rules (${verdict.reason}). Mintlify does not enforce groups on snippets, but it was NOT inlined to be safe; it was left as a comment.`,
|
|
7477
|
+
source
|
|
7478
|
+
});
|
|
7479
|
+
return `{/* Access-restricted content withheld: ${shown} */}`;
|
|
7480
|
+
}
|
|
7481
|
+
warnings.push({
|
|
7482
|
+
code: "skipped-file",
|
|
7483
|
+
message: `${label} ${verdict.kind === "oversized" ? "is too large to inline (over 2 MB)" : "has frontmatter that could not be read"} and was NOT inlined; it was left as a comment.`,
|
|
7484
|
+
source
|
|
7485
|
+
});
|
|
7486
|
+
return `{/* ${verdict.kind === "oversized" ? "Oversized" : "Unreadable"} content not inlined: ${shown} */}`;
|
|
7487
|
+
};
|
|
7488
|
+
let withoutImports = replaceOutsideCode(raw, (source2) => source2.replace(
|
|
7057
7489
|
SNIPPET_IMPORT_PATTERN,
|
|
7058
7490
|
(_statement, namedComponent, defaultComponent, sourcePath) => {
|
|
7059
7491
|
const componentName = namedComponent ?? defaultComponent;
|
|
7060
7492
|
try {
|
|
7061
7493
|
const candidate = resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot);
|
|
7062
7494
|
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile()) throw new Error("file not found");
|
|
7495
|
+
const blocked = blockInline(candidate, sourcePath, `Imported ${sourcePath}`);
|
|
7496
|
+
if (blocked) {
|
|
7497
|
+
snippets.set(componentName, blocked);
|
|
7498
|
+
return "";
|
|
7499
|
+
}
|
|
7063
7500
|
const nested = inlineMdxSnippets(
|
|
7064
7501
|
withoutFrontmatter(readFileSync5(candidate, "utf8")),
|
|
7065
7502
|
candidate,
|
|
@@ -7067,7 +7504,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7067
7504
|
warnings,
|
|
7068
7505
|
depth + 1,
|
|
7069
7506
|
siteRoot,
|
|
7070
|
-
globalAliases
|
|
7507
|
+
globalAliases,
|
|
7508
|
+
gate
|
|
7071
7509
|
);
|
|
7072
7510
|
const declaration = statefulSnippetDeclaration(nested, componentName);
|
|
7073
7511
|
if (declaration) preservedDeclarations.set(componentName, declaration);
|
|
@@ -7084,7 +7522,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7084
7522
|
}
|
|
7085
7523
|
}
|
|
7086
7524
|
));
|
|
7087
|
-
withoutImports = replaceOutsideCode(withoutImports, (
|
|
7525
|
+
withoutImports = replaceOutsideCode(withoutImports, (source2) => source2.replace(
|
|
7088
7526
|
SNIPPET_VALUE_IMPORT_PATTERN,
|
|
7089
7527
|
(statement, names, sourcePath) => {
|
|
7090
7528
|
const bindings = names.split(",").map((name) => name.trim()).filter(Boolean).map((name) => {
|
|
@@ -7094,7 +7532,16 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7094
7532
|
if (bindings.length === 0 || bindings.some((binding) => !binding)) return statement;
|
|
7095
7533
|
try {
|
|
7096
7534
|
const candidate = resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot);
|
|
7097
|
-
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile()) return statement;
|
|
7535
|
+
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile() || gate?.(candidate)?.kind === "gated") return statement;
|
|
7536
|
+
const blocked = blockInline(candidate, sourcePath);
|
|
7537
|
+
if (blocked) {
|
|
7538
|
+
const declarations2 = [];
|
|
7539
|
+
for (const binding of bindings) {
|
|
7540
|
+
if (/^[A-Z]/.test(binding.exported)) snippets.set(binding.local, blocked);
|
|
7541
|
+
else declarations2.push(`export const ${binding.local} = undefined;`);
|
|
7542
|
+
}
|
|
7543
|
+
return declarations2.join("\n");
|
|
7544
|
+
}
|
|
7098
7545
|
const snippetSource = readFileSync5(candidate, "utf8");
|
|
7099
7546
|
const values = staticNamedSnippetValues(snippetSource);
|
|
7100
7547
|
const declarations = [];
|
|
@@ -7119,7 +7566,12 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7119
7566
|
));
|
|
7120
7567
|
const localNames = locallyDeclaredNames(withoutImports);
|
|
7121
7568
|
for (const [componentName, candidate] of globalAliases) {
|
|
7122
|
-
if (snippets.has(componentName) || localNames.has(componentName) || !new RegExp(`<${componentName}(?:\\s|/?>)`).test(withoutImports)) continue;
|
|
7569
|
+
if (snippets.has(componentName) || localNames.has(componentName) || gate?.(candidate)?.kind === "gated" || !new RegExp(`<${componentName}(?:\\s|/?>)`).test(withoutImports)) continue;
|
|
7570
|
+
const blocked = blockInline(candidate, relative5(repositoryRoot, candidate).replace(/\\/g, "/"));
|
|
7571
|
+
if (blocked) {
|
|
7572
|
+
snippets.set(componentName, blocked);
|
|
7573
|
+
continue;
|
|
7574
|
+
}
|
|
7123
7575
|
const nested = inlineMdxSnippets(
|
|
7124
7576
|
withoutFrontmatter(readFileSync5(candidate, "utf8")),
|
|
7125
7577
|
candidate,
|
|
@@ -7127,7 +7579,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7127
7579
|
warnings,
|
|
7128
7580
|
depth + 1,
|
|
7129
7581
|
siteRoot,
|
|
7130
|
-
globalAliases
|
|
7582
|
+
globalAliases,
|
|
7583
|
+
gate
|
|
7131
7584
|
);
|
|
7132
7585
|
const declaration = statefulSnippetDeclaration(nested, componentName);
|
|
7133
7586
|
if (declaration) preservedDeclarations.set(componentName, declaration);
|
|
@@ -7155,6 +7608,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7155
7608
|
}
|
|
7156
7609
|
}).find((path) => path !== void 0 && existsSync5(path) && lstatSync5(path).isFile());
|
|
7157
7610
|
if (!candidate) throw new Error("file not found");
|
|
7611
|
+
const blocked = blockInline(candidate, filePath, `Snippet file="${filePath}"`);
|
|
7612
|
+
if (blocked) return blocked;
|
|
7158
7613
|
return inlineMdxSnippets(
|
|
7159
7614
|
withoutFrontmatter(readFileSync5(candidate, "utf8")),
|
|
7160
7615
|
candidate,
|
|
@@ -7162,7 +7617,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
7162
7617
|
warnings,
|
|
7163
7618
|
depth + 1,
|
|
7164
7619
|
siteRoot,
|
|
7165
|
-
globalAliases
|
|
7620
|
+
globalAliases,
|
|
7621
|
+
gate
|
|
7166
7622
|
);
|
|
7167
7623
|
} catch {
|
|
7168
7624
|
warnings.push({
|
|
@@ -7254,6 +7710,7 @@ function migrateRepository(options) {
|
|
|
7254
7710
|
const referenceOrder = /* @__PURE__ */ new Map();
|
|
7255
7711
|
let docusaurusSidebars = null;
|
|
7256
7712
|
let mintlifyConfig = null;
|
|
7713
|
+
const mintlifyGatedRefs = /* @__PURE__ */ new Map();
|
|
7257
7714
|
let fernRawConfig = null;
|
|
7258
7715
|
let fernBasePath = "";
|
|
7259
7716
|
let fernVersionPath = "";
|
|
@@ -7274,6 +7731,10 @@ function migrateRepository(options) {
|
|
|
7274
7731
|
const projected = projectMintlifyNavigation(config);
|
|
7275
7732
|
docsConfig = projected.docsConfig;
|
|
7276
7733
|
warnings.push(...projected.warnings);
|
|
7734
|
+
for (const gated of projected.gatedReferences) {
|
|
7735
|
+
const gatedKey = normalizedReferenceKey(gated.ref).toLowerCase();
|
|
7736
|
+
if (!mintlifyGatedRefs.has(gatedKey)) mintlifyGatedRefs.set(gatedKey, gated.reason);
|
|
7737
|
+
}
|
|
7277
7738
|
for (const [index, reference] of projected.pageReferences.entries()) {
|
|
7278
7739
|
const key = normalizedReferenceKey(reference.ref);
|
|
7279
7740
|
if (!referenceMap.has(key)) referenceMap.set(key, reference);
|
|
@@ -7359,10 +7820,11 @@ function migrateRepository(options) {
|
|
|
7359
7820
|
const versionCeiling = lastReferencedIndexByVersionLocale.get(blockKey);
|
|
7360
7821
|
return versionCeiling !== void 0 ? versionCeiling + 1 : Number.MAX_SAFE_INTEGER;
|
|
7361
7822
|
} : void 0;
|
|
7362
|
-
const
|
|
7823
|
+
const sourceBudget = options.maxSourceFiles ?? MAX_SOURCE_FILES;
|
|
7824
|
+
const scannedFiles = scanFiles(contentRoot, repositoryDir, warnings, discoveryRank, sourceBudget);
|
|
7363
7825
|
const mintignoreFilteredFiles = mintignoreMatcher ? scannedFiles.filter((file) => !mintignoreMatcher.ignores(file.relativePath)) : scannedFiles;
|
|
7364
|
-
const discoveryBudgetApplied = discoveryRank !== void 0 && mintignoreFilteredFiles.length >
|
|
7365
|
-
const files = discoveryRank ? selectFilesWithinBudget(mintignoreFilteredFiles, discoveryRank, warnings, allVersionPrefixes) : mintignoreFilteredFiles;
|
|
7826
|
+
const discoveryBudgetApplied = discoveryRank !== void 0 && mintignoreFilteredFiles.length > sourceBudget;
|
|
7827
|
+
const files = discoveryRank ? selectFilesWithinBudget(mintignoreFilteredFiles, discoveryRank, warnings, allVersionPrefixes, sourceBudget) : mintignoreFilteredFiles;
|
|
7366
7828
|
if (platform === "fern" && fernProjectRoot) {
|
|
7367
7829
|
const discovered2 = new Set(files.map((file) => file.relativePath));
|
|
7368
7830
|
for (const sourcePath of fernExternalSourcePaths) {
|
|
@@ -7392,6 +7854,108 @@ function migrateRepository(options) {
|
|
|
7392
7854
|
const docusaurusDescriptors = [];
|
|
7393
7855
|
const docusaurusDocCardRoutes = /* @__PURE__ */ new Set();
|
|
7394
7856
|
const seenPageIds = /* @__PURE__ */ new Set();
|
|
7857
|
+
const quarantinedFiles = [];
|
|
7858
|
+
const withheldAssetPaths = /* @__PURE__ */ new Set();
|
|
7859
|
+
const publishedExact = /* @__PURE__ */ new Set();
|
|
7860
|
+
const publishedLoose = /* @__PURE__ */ new Set();
|
|
7861
|
+
const withholdAsset = (assetPath, onDiskSpelling) => {
|
|
7862
|
+
withheldAssetPaths.add(publicAssetKey(onDiskSpelling ?? assetPath));
|
|
7863
|
+
};
|
|
7864
|
+
let sawPublicTrue = false;
|
|
7865
|
+
const gateByPath = /* @__PURE__ */ new Map();
|
|
7866
|
+
const withheldSpecRefs = [];
|
|
7867
|
+
const publishedSpecRefs = [];
|
|
7868
|
+
const withheldPaths = /* @__PURE__ */ new Set();
|
|
7869
|
+
const isDocFile = (file) => [".md", ".mdx"].includes(extname6(file.relativePath).toLowerCase());
|
|
7870
|
+
const isGateCandidate = (file) => isDocFile(file) && !file.relativePath.split("/").some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase()));
|
|
7871
|
+
const classifyPageGate = (file) => {
|
|
7872
|
+
let head = { publicTrue: false };
|
|
7873
|
+
let unreadableGate;
|
|
7874
|
+
try {
|
|
7875
|
+
const size = lstatSync5(file.absolutePath).size;
|
|
7876
|
+
const raw = readFrontmatterHead(file.absolutePath) ?? (size > MAX_PAGE_BYTES ? void 0 : readFileSync5(file.absolutePath, "utf8"));
|
|
7877
|
+
if (raw === void 0) throw new Error("frontmatter is not terminated within the bounded read");
|
|
7878
|
+
head = pageHeadGateReason(raw);
|
|
7879
|
+
} catch {
|
|
7880
|
+
unreadableGate = "frontmatter could not be read";
|
|
7881
|
+
}
|
|
7882
|
+
const reason = head.reason ?? unreadableGate ?? mintlifyGatedRefs.get(normalizedReferenceKey(file.relativePath).toLowerCase());
|
|
7883
|
+
return { reason, publicTrue: head.publicTrue, openapi: head.openapi };
|
|
7884
|
+
};
|
|
7885
|
+
const keptFiles = new Set(files);
|
|
7886
|
+
const droppedGatedFiles = [];
|
|
7887
|
+
const withheldDocFiles = [];
|
|
7888
|
+
let droppedPageCount = 0;
|
|
7889
|
+
const scanTruncated = scannedFiles.length >= (discoveryRank ? sourceBudget * RANKED_WALK_FACTOR : sourceBudget);
|
|
7890
|
+
if (platform === "mintlify") {
|
|
7891
|
+
for (const file of files) {
|
|
7892
|
+
if (isDocFile(file) && !isGateCandidate(file)) {
|
|
7893
|
+
const snippetReason = classifyPageGate(file).reason;
|
|
7894
|
+
if (snippetReason) withheldDocFiles.push(file);
|
|
7895
|
+
continue;
|
|
7896
|
+
}
|
|
7897
|
+
if (!isGateCandidate(file)) continue;
|
|
7898
|
+
const verdict = classifyPageGate(file);
|
|
7899
|
+
gateByPath.set(file.absolutePath, { reason: verdict.reason, publicTrue: verdict.publicTrue });
|
|
7900
|
+
if (verdict.openapi) (verdict.reason ? withheldSpecRefs : publishedSpecRefs).push(verdict.openapi);
|
|
7901
|
+
if (verdict.reason) withheldPaths.add(file.absolutePath);
|
|
7902
|
+
}
|
|
7903
|
+
for (const file of mintignoreFilteredFiles) {
|
|
7904
|
+
if (keptFiles.has(file) || !isDocFile(file)) continue;
|
|
7905
|
+
droppedPageCount++;
|
|
7906
|
+
if (!isGateCandidate(file)) {
|
|
7907
|
+
const snippetReason = classifyPageGate(file).reason;
|
|
7908
|
+
if (snippetReason) {
|
|
7909
|
+
withheldDocFiles.push(file);
|
|
7910
|
+
warnings.push({ code: "gated-page", message: `Access-restricted snippet (${snippetReason}) was dropped by the file limit; its assets were kept out of public/.`, source: file.relativePath });
|
|
7911
|
+
}
|
|
7912
|
+
continue;
|
|
7913
|
+
}
|
|
7914
|
+
const verdict = classifyPageGate(file);
|
|
7915
|
+
if (!verdict.reason) continue;
|
|
7916
|
+
if (verdict.openapi) withheldSpecRefs.push(verdict.openapi);
|
|
7917
|
+
withheldPaths.add(file.absolutePath);
|
|
7918
|
+
droppedGatedFiles.push({ file, reason: verdict.reason });
|
|
7919
|
+
}
|
|
7920
|
+
for (const file of scannedFiles) {
|
|
7921
|
+
if (!mintignoreMatcher?.ignores(file.relativePath) || !isDocFile(file)) continue;
|
|
7922
|
+
const verdict = classifyPageGate(file);
|
|
7923
|
+
if (!verdict.reason) continue;
|
|
7924
|
+
if (verdict.openapi) withheldSpecRefs.push(verdict.openapi);
|
|
7925
|
+
withheldDocFiles.push(file);
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7928
|
+
const pagesNotClassified = droppedPageCount > 0 || platform === "mintlify" && scanTruncated;
|
|
7929
|
+
const hasWithheldContent = withheldPaths.size > 0 || withheldDocFiles.length > 0;
|
|
7930
|
+
const trackPublishedRefs = platform === "mintlify" && (hasWithheldContent || pagesNotClassified);
|
|
7931
|
+
const withheldCanonical = new Set([...withheldPaths].map(canonicalPath));
|
|
7932
|
+
const verdictCache = /* @__PURE__ */ new Map();
|
|
7933
|
+
const classifyInlineCandidate = (candidate, key) => {
|
|
7934
|
+
if (withheldCanonical.has(key)) return { kind: "gated", reason: "access-restricted" };
|
|
7935
|
+
const shown = relative5(contentRoot, candidate).replace(/\\/g, "/");
|
|
7936
|
+
const inSnippetDirectory = shown.split("/").some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase()));
|
|
7937
|
+
let head;
|
|
7938
|
+
try {
|
|
7939
|
+
head = readFrontmatterHead(candidate);
|
|
7940
|
+
} catch {
|
|
7941
|
+
return { kind: "unreadable", reason: "could not be read" };
|
|
7942
|
+
}
|
|
7943
|
+
const gated = head === void 0 ? void 0 : pageHeadGateReason(head).reason;
|
|
7944
|
+
if (gated) return { kind: inSnippetDirectory ? "snippet-gated" : "gated", reason: gated };
|
|
7945
|
+
const navigationGated = inSnippetDirectory ? void 0 : mintlifyGatedRefs.get(normalizedReferenceKey(shown).toLowerCase());
|
|
7946
|
+
if (navigationGated) return { kind: "gated", reason: navigationGated };
|
|
7947
|
+
if (lstatSync5(candidate).size > MAX_PAGE_BYTES) return { kind: "oversized", reason: "over 2 MB" };
|
|
7948
|
+
return head === void 0 ? { kind: "unreadable", reason: "frontmatter not terminated in the bounded read" } : void 0;
|
|
7949
|
+
};
|
|
7950
|
+
const inlineGate = platform === "mintlify" ? (candidate) => {
|
|
7951
|
+
const key = canonicalPath(candidate);
|
|
7952
|
+
let verdict = verdictCache.get(key);
|
|
7953
|
+
if (verdict === void 0) {
|
|
7954
|
+
verdict = classifyInlineCandidate(candidate, key) ?? null;
|
|
7955
|
+
verdictCache.set(key, verdict);
|
|
7956
|
+
}
|
|
7957
|
+
return verdict ?? void 0;
|
|
7958
|
+
} : void 0;
|
|
7395
7959
|
const fernIdRenames = /* @__PURE__ */ new Map();
|
|
7396
7960
|
let skipped = 0;
|
|
7397
7961
|
let discovered = files.length;
|
|
@@ -7404,6 +7968,47 @@ function migrateRepository(options) {
|
|
|
7404
7968
|
return leftOrder - rightOrder || left.relativePath.localeCompare(right.relativePath);
|
|
7405
7969
|
});
|
|
7406
7970
|
const snippetAliases = platform === "mintlify" && mintlifyProjectRoot ? globalSnippetAliases(files, repositoryDir, mintlifyProjectRoot) : /* @__PURE__ */ new Map();
|
|
7971
|
+
droppedGatedFiles.forEach(({ file, reason }, index) => {
|
|
7972
|
+
if (index < MAX_DROPPED_GATED_WARNINGS) {
|
|
7973
|
+
warnings.push({
|
|
7974
|
+
code: "gated-page",
|
|
7975
|
+
message: `Access-restricted page (${reason}) was dropped by the file limit, so it was NOT published and was not saved under ${QUARANTINE_DIRECTORY}/; recover it from the source repository.`,
|
|
7976
|
+
source: file.relativePath
|
|
7977
|
+
});
|
|
7978
|
+
}
|
|
7979
|
+
if (!mintlifyProjectRoot) return;
|
|
7980
|
+
try {
|
|
7981
|
+
if (lstatSync5(file.absolutePath).size > MAX_WITHHELD_SCAN_BYTES) throw new Error("too large to scan");
|
|
7982
|
+
rewriteRepositoryAssetLinks(
|
|
7983
|
+
inlineMdxSnippets(readFileSync5(file.absolutePath, "utf8"), file.absolutePath, repositoryDir, [], 0, mintlifyProjectRoot, snippetAliases, inlineGate),
|
|
7984
|
+
file.absolutePath,
|
|
7985
|
+
mintlifyProjectRoot,
|
|
7986
|
+
withholdAsset
|
|
7987
|
+
);
|
|
7988
|
+
} catch {
|
|
7989
|
+
}
|
|
7990
|
+
});
|
|
7991
|
+
for (const file of withheldDocFiles) {
|
|
7992
|
+
try {
|
|
7993
|
+
if (!mintlifyProjectRoot) continue;
|
|
7994
|
+
if (lstatSync5(file.absolutePath).size > MAX_WITHHELD_SCAN_BYTES) {
|
|
7995
|
+
warnings.push({
|
|
7996
|
+
code: "gated-page",
|
|
7997
|
+
message: "Access-restricted file is too large to scan, so the assets it uses could not be checked and may have been copied to public/. Review them before publishing.",
|
|
7998
|
+
source: file.relativePath
|
|
7999
|
+
});
|
|
8000
|
+
continue;
|
|
8001
|
+
}
|
|
8002
|
+
rewriteRepositoryAssetLinks(readFileSync5(file.absolutePath, "utf8"), file.absolutePath, mintlifyProjectRoot, withholdAsset);
|
|
8003
|
+
} catch {
|
|
8004
|
+
}
|
|
8005
|
+
}
|
|
8006
|
+
if (droppedGatedFiles.length > MAX_DROPPED_GATED_WARNINGS) {
|
|
8007
|
+
warnings.push({
|
|
8008
|
+
code: "gated-page",
|
|
8009
|
+
message: `${droppedGatedFiles.length - MAX_DROPPED_GATED_WARNINGS} more access-restricted page(s) were dropped by the file limit and NOT published; recover them from the source repository.`
|
|
8010
|
+
});
|
|
8011
|
+
}
|
|
7407
8012
|
for (const file of pageFiles) {
|
|
7408
8013
|
if (!isDocumentationExtension(file.relativePath)) continue;
|
|
7409
8014
|
if (platform === "fern" && !exactReferenceMap.has(exactReferenceKey(file.relativePath)) && !referenceMap.has(normalizedReferenceKey(file.relativePath))) {
|
|
@@ -7420,7 +8025,7 @@ function migrateRepository(options) {
|
|
|
7420
8025
|
skipped++;
|
|
7421
8026
|
continue;
|
|
7422
8027
|
}
|
|
7423
|
-
if (![".md", ".mdx"].includes(
|
|
8028
|
+
if (![".md", ".mdx"].includes(extname6(file.relativePath).toLowerCase())) {
|
|
7424
8029
|
skipped++;
|
|
7425
8030
|
warnings.push({ code: "skipped-file", message: "Only Markdown and MDX are imported without an explicit converter.", source: file.relativePath });
|
|
7426
8031
|
continue;
|
|
@@ -7429,9 +8034,64 @@ function migrateRepository(options) {
|
|
|
7429
8034
|
if (size > MAX_PAGE_BYTES) {
|
|
7430
8035
|
skipped++;
|
|
7431
8036
|
warnings.push({ code: "skipped-file", message: "Page exceeded the 2 MB repository import limit.", source: file.relativePath });
|
|
8037
|
+
const oversizedGate = gateByPath.get(file.absolutePath)?.reason;
|
|
8038
|
+
if (oversizedGate) {
|
|
8039
|
+
warnings.push({
|
|
8040
|
+
code: "gated-page",
|
|
8041
|
+
message: `Access-restricted page (${oversizedGate}) is too large to migrate (over 2 MB), so it was NOT published and was not saved under ${QUARANTINE_DIRECTORY}/; recover it from the source repository.`,
|
|
8042
|
+
source: file.relativePath
|
|
8043
|
+
});
|
|
8044
|
+
}
|
|
8045
|
+
if (oversizedGate && mintlifyProjectRoot) {
|
|
8046
|
+
if (size > MAX_WITHHELD_SCAN_BYTES) {
|
|
8047
|
+
warnings.push({
|
|
8048
|
+
code: "gated-page",
|
|
8049
|
+
message: "Access-restricted page is too large to scan, so the assets it uses could not be checked and may have been copied to public/. Review them before publishing.",
|
|
8050
|
+
source: file.relativePath
|
|
8051
|
+
});
|
|
8052
|
+
} else {
|
|
8053
|
+
rewriteRepositoryAssetLinks(readFileSync5(file.absolutePath, "utf8"), file.absolutePath, mintlifyProjectRoot, withholdAsset);
|
|
8054
|
+
}
|
|
8055
|
+
}
|
|
7432
8056
|
continue;
|
|
7433
8057
|
}
|
|
7434
8058
|
const key = normalizedReferenceKey(file.relativePath);
|
|
8059
|
+
if (platform === "mintlify") {
|
|
8060
|
+
const gate = gateByPath.get(file.absolutePath);
|
|
8061
|
+
if (gate?.publicTrue) sawPublicTrue = true;
|
|
8062
|
+
const gateReason = gate?.reason;
|
|
8063
|
+
if (gateReason) {
|
|
8064
|
+
skipped++;
|
|
8065
|
+
if (file.relativePath.split("/").some((segment) => segment === "..")) {
|
|
8066
|
+
warnings.push({ code: "gated-page", message: `Access-restricted page (${gateReason}) was withheld from the site, but its path could not be preserved safely; recover it from the source repository.`, source: file.relativePath });
|
|
8067
|
+
continue;
|
|
8068
|
+
}
|
|
8069
|
+
try {
|
|
8070
|
+
if (mintlifyProjectRoot) {
|
|
8071
|
+
rewriteRepositoryAssetLinks(
|
|
8072
|
+
inlineMdxSnippets(readFileSync5(file.absolutePath, "utf8"), file.absolutePath, repositoryDir, [], 0, mintlifyProjectRoot, snippetAliases, inlineGate),
|
|
8073
|
+
file.absolutePath,
|
|
8074
|
+
mintlifyProjectRoot,
|
|
8075
|
+
withholdAsset
|
|
8076
|
+
);
|
|
8077
|
+
}
|
|
8078
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/${file.relativePath}`, content: readFileSync5(file.absolutePath) });
|
|
8079
|
+
} catch {
|
|
8080
|
+
warnings.push({
|
|
8081
|
+
code: "gated-page",
|
|
8082
|
+
message: `Access-restricted page (${gateReason}) was NOT published, but it could not be read, so it was not copied to ${QUARANTINE_DIRECTORY}/; recover it from the source repository.`,
|
|
8083
|
+
source: file.relativePath
|
|
8084
|
+
});
|
|
8085
|
+
continue;
|
|
8086
|
+
}
|
|
8087
|
+
warnings.push({
|
|
8088
|
+
code: "gated-page",
|
|
8089
|
+
message: `Access-restricted on the source site (${gateReason}), so it was NOT published. The original is saved at ${QUARANTINE_DIRECTORY}/${file.relativePath}; links from other pages to it will break.`,
|
|
8090
|
+
source: file.relativePath
|
|
8091
|
+
});
|
|
8092
|
+
continue;
|
|
8093
|
+
}
|
|
8094
|
+
}
|
|
7435
8095
|
const referenced = exactReferenceMap.get(exactReferenceKey(file.relativePath)) ?? referenceMap.get(key);
|
|
7436
8096
|
let locale = referenced?.locale;
|
|
7437
8097
|
let navigationId = referenced?.navigationId ?? pageIdFromReference(file.relativePath, platform === "mintlify");
|
|
@@ -7458,8 +8118,11 @@ function migrateRepository(options) {
|
|
|
7458
8118
|
warnings,
|
|
7459
8119
|
0,
|
|
7460
8120
|
mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir,
|
|
7461
|
-
snippetAliases
|
|
8121
|
+
snippetAliases,
|
|
8122
|
+
inlineGate
|
|
7462
8123
|
);
|
|
8124
|
+
const publishedText = trackPublishedRefs ? raw : void 0;
|
|
8125
|
+
const pageAssetReferences = [];
|
|
7463
8126
|
if (platform === "fern" || platform === "mintlify" || platform === "docusaurus") {
|
|
7464
8127
|
raw = normalizeExplicitHeadingIds(raw);
|
|
7465
8128
|
}
|
|
@@ -7551,8 +8214,8 @@ function migrateRepository(options) {
|
|
|
7551
8214
|
}
|
|
7552
8215
|
if (platform === "fern" && fernHiddenIds.has(navigationId)) page.hidden = true;
|
|
7553
8216
|
if (platform === "mintlify" && mintlifyProjectRoot) {
|
|
7554
|
-
page.body = rewriteRepositoryAssetLinks(page.body, file.absolutePath, mintlifyProjectRoot, (assetPath) => {
|
|
7555
|
-
|
|
8217
|
+
page.body = rewriteRepositoryAssetLinks(page.body, file.absolutePath, mintlifyProjectRoot, (assetPath, onDiskSpelling) => {
|
|
8218
|
+
pageAssetReferences.push(publicAssetKey(onDiskSpelling ?? assetPath));
|
|
7556
8219
|
});
|
|
7557
8220
|
}
|
|
7558
8221
|
if (platform === "fern" && fernProjectRoot) {
|
|
@@ -7616,6 +8279,8 @@ function migrateRepository(options) {
|
|
|
7616
8279
|
}
|
|
7617
8280
|
seenPageIds.add(page.id);
|
|
7618
8281
|
pages.push(page);
|
|
8282
|
+
for (const assetPath of pageAssetReferences) addAssetReference(assetPath, file.relativePath);
|
|
8283
|
+
if (publishedText !== void 0) addPathReferences(publishedText, posix3.dirname(file.relativePath).replace(/^\.$/, ""), publishedExact, publishedLoose);
|
|
7619
8284
|
if (hasDocusaurusDocCardList) {
|
|
7620
8285
|
docusaurusDocCardRoutes.add(page.navigationId);
|
|
7621
8286
|
}
|
|
@@ -7658,7 +8323,7 @@ function migrateRepository(options) {
|
|
|
7658
8323
|
const withinFern = relative5(fernProjectRoot, absolutePath);
|
|
7659
8324
|
const assetRelative = withinFern !== ".." && !withinFern.startsWith(`..${sep2}`) && !isAbsolute2(withinFern) ? withinFern : relative5(repositoryDir, absolutePath);
|
|
7660
8325
|
const assetPath = normalizeAssetPath(assetRelative.replace(/\\/g, "/"));
|
|
7661
|
-
if (!assetPath || !ASSET_EXTENSIONS.has(
|
|
8326
|
+
if (!assetPath || !ASSET_EXTENSIONS.has(extname6(assetPath).toLowerCase())) return;
|
|
7662
8327
|
if (source) {
|
|
7663
8328
|
fernBrandAssetPaths.set(source, assetPath);
|
|
7664
8329
|
addAssetReference(assetPath, "fern/docs.yml");
|
|
@@ -7689,24 +8354,59 @@ function migrateRepository(options) {
|
|
|
7689
8354
|
}));
|
|
7690
8355
|
}) : [];
|
|
7691
8356
|
const assetCandidates = [];
|
|
8357
|
+
const mintlifyFonts = platform === "mintlify" ? mintlifyFontSources(mintlifyConfig) : [];
|
|
8358
|
+
const fontRelativePaths = new Set(mintlifyFonts.flatMap((font) => font.path ? [font.path] : []));
|
|
8359
|
+
const importedComponentSources = new Set((componentMigrator?.files() ?? []).flatMap((file) => {
|
|
8360
|
+
const source = /^src\/mdx\/migrated\/[^/]+\/source\/(.+)$/.exec(file.path)?.[1];
|
|
8361
|
+
return source ? [resolvePath(repositoryDir, source)] : [];
|
|
8362
|
+
}));
|
|
8363
|
+
const siteAssetPaths = /* @__PURE__ */ new Map();
|
|
8364
|
+
const fontAssetByRelative = /* @__PURE__ */ new Map();
|
|
7692
8365
|
for (const file of [...files, ...repositoryAssets, ...fernReferencedAssets]) {
|
|
7693
8366
|
const firstSegment = file.relativePath.split("/", 1)[0].toLowerCase();
|
|
7694
|
-
|
|
8367
|
+
const siteKind = platform !== "mintlify" ? void 0 : fontRelativePaths.has(file.relativePath) ? "font" : isMintlifyServedScriptOrStyle(file.relativePath) && !importedComponentSources.has(file.absolutePath) ? extname6(file.relativePath).toLowerCase() === ".css" ? "style" : "script" : void 0;
|
|
8368
|
+
if (!siteKind && !ASSET_EXTENSIONS.has(extname6(file.relativePath).toLowerCase())) continue;
|
|
7695
8369
|
if (platform !== "mintlify" && !ASSET_DIRECTORIES.has(firstSegment) && !(platform === "fern" && referencedAssetPaths.has(file.relativePath))) continue;
|
|
7696
8370
|
const isDocusaurusStatic = platform === "docusaurus" && firstSegment === "static";
|
|
7697
8371
|
const assetPath = normalizeAssetPath(firstSegment === "public" ? file.relativePath.slice("public/".length) : isDocusaurusStatic ? file.relativePath.slice("static/".length) : file.relativePath);
|
|
7698
8372
|
if (!assetPath) continue;
|
|
7699
|
-
|
|
8373
|
+
if (siteKind) {
|
|
8374
|
+
siteAssetPaths.set(assetPath, siteKind);
|
|
8375
|
+
if (siteKind === "font") fontAssetByRelative.set(file.relativePath, assetPath);
|
|
8376
|
+
}
|
|
8377
|
+
const candidateSize = lstatSync5(file.absolutePath).size;
|
|
8378
|
+
if (siteKind === "style" || siteKind === "script") {
|
|
8379
|
+
if (trackPublishedRefs && candidateSize <= MAX_PAGE_BYTES) addPathReferences(readFileSync5(file.absolutePath, "utf8"), posix3.dirname(file.relativePath).replace(/^\.$/, ""), publishedExact, publishedLoose);
|
|
8380
|
+
}
|
|
8381
|
+
assetCandidates.push({ file, assetPath, size: candidateSize });
|
|
7700
8382
|
}
|
|
7701
8383
|
const isReferenced = (candidate) => referencedAssetPaths.has(candidate.assetPath);
|
|
7702
8384
|
const orderedAssetCandidates = [
|
|
7703
8385
|
...assetCandidates.filter((candidate) => isReferenced(candidate)),
|
|
7704
8386
|
...assetCandidates.filter((candidate) => !isReferenced(candidate))
|
|
7705
8387
|
];
|
|
8388
|
+
const quarantinedPageCount = quarantinedFiles.length;
|
|
8389
|
+
const destinationCounts = /* @__PURE__ */ new Map();
|
|
8390
|
+
for (const { assetPath } of assetCandidates) destinationCounts.set(assetPath, (destinationCounts.get(assetPath) ?? 0) + 1);
|
|
8391
|
+
if (trackPublishedRefs) {
|
|
8392
|
+
const publishedConfig = JSON.stringify(mintlifyConfig ?? {}, (_key, value) => value && typeof value === "object" && !Array.isArray(value) && navigationGateReason(value) ? void 0 : value);
|
|
8393
|
+
addPathReferences(publishedConfig, "", publishedExact, publishedLoose);
|
|
8394
|
+
}
|
|
8395
|
+
for (const file of componentMigrator?.files() ?? []) {
|
|
8396
|
+
if (!trackPublishedRefs || typeof file.content !== "string") continue;
|
|
8397
|
+
const original = /^src\/mdx\/migrated\/[^/]+\/source\/(.+)$/.exec(file.path)?.[1];
|
|
8398
|
+
addPathReferences(file.content, original === void 0 ? "" : posix3.dirname(original).replace(/^\.$/, ""), publishedExact, publishedLoose);
|
|
8399
|
+
}
|
|
8400
|
+
const failClosedAssets = trackPublishedRefs;
|
|
8401
|
+
const isPublicReachable = (assetPath) => referencedAssetPaths.has(assetPath) || publishedExact.has(assetPath);
|
|
8402
|
+
const isQuarantinedAsset = (assetPath) => (withheldAssetPaths.has(assetPath) || failClosedAssets && !siteAssetPaths.has(assetPath)) && platform === "mintlify" && !(isPublicReachable(assetPath) && !(failClosedAssets && (destinationCounts.get(assetPath) ?? 0) > 1));
|
|
8403
|
+
let withheldAssetCount = 0;
|
|
8404
|
+
let unreferencedAssetCount = 0;
|
|
7706
8405
|
let totalAssetBytes = 0;
|
|
7707
8406
|
const overBudgetAssets = [];
|
|
7708
8407
|
let overBudgetReferenced = 0;
|
|
7709
8408
|
const lfsPointerAssets = [];
|
|
8409
|
+
const ambiguouslyNamedAssets = [];
|
|
7710
8410
|
for (const { file, assetPath, size } of orderedAssetCandidates) {
|
|
7711
8411
|
if (size > MAX_ASSET_BYTES || totalAssetBytes + size > MAX_TOTAL_ASSET_BYTES) {
|
|
7712
8412
|
overBudgetAssets.push(file.relativePath);
|
|
@@ -7718,9 +8418,53 @@ function migrateRepository(options) {
|
|
|
7718
8418
|
lfsPointerAssets.push(file.relativePath);
|
|
7719
8419
|
continue;
|
|
7720
8420
|
}
|
|
8421
|
+
if (isQuarantinedAsset(assetPath)) {
|
|
8422
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${assetPath}`, content });
|
|
8423
|
+
if (looselyNamed(assetPath, publishedLoose) || (destinationCounts.get(assetPath) ?? 0) > 1) ambiguouslyNamedAssets.push(assetPath);
|
|
8424
|
+
if (withheldAssetPaths.has(assetPath)) withheldAssetCount++;
|
|
8425
|
+
else unreferencedAssetCount++;
|
|
8426
|
+
continue;
|
|
8427
|
+
}
|
|
7721
8428
|
assets.push({ path: assetPath, content });
|
|
7722
8429
|
totalAssetBytes += size;
|
|
7723
8430
|
}
|
|
8431
|
+
if (ambiguouslyNamedAssets.length > 0) {
|
|
8432
|
+
warnings.push({
|
|
8433
|
+
code: "gated-page",
|
|
8434
|
+
message: `${ambiguouslyNamedAssets.length} asset(s) were kept out of public/ because a published page names a file like ${ambiguouslyNamedAssets.length === 1 ? "it" : "them"} in a way that does not give its folder (or differs in letter case), so it cannot be matched to one file (or two files share one public path): ${listAssetPaths(ambiguouslyNamedAssets)}. Copy any that published pages need from ${QUARANTINE_DIRECTORY}/assets/ into public/ by hand.`
|
|
8435
|
+
});
|
|
8436
|
+
}
|
|
8437
|
+
if (platform === "mintlify") {
|
|
8438
|
+
const copiedSite = assets.flatMap((asset) => siteAssetPaths.has(asset.path) ? [{ path: asset.path, kind: siteAssetPaths.get(asset.path) }] : []);
|
|
8439
|
+
const scripts = copiedSite.filter((entry) => entry.kind === "script");
|
|
8440
|
+
if (scripts.length > 0) {
|
|
8441
|
+
docsConfig = {
|
|
8442
|
+
...docsConfig,
|
|
8443
|
+
customScripts: [...docsConfig.customScripts ?? [], ...scripts.map((entry) => ({ src: `/${entry.path.split("/").map(encodeURIComponent).join("/")}`, strategy: "afterInteractive" }))]
|
|
8444
|
+
};
|
|
8445
|
+
warnings.push({
|
|
8446
|
+
code: "unsupported-config",
|
|
8447
|
+
message: `${scripts.length} script(s) from the content directory (${scripts.map((entry) => entry.path).join(", ")}) now load on every page via docs.json customScripts, as they did on Mintlify. They may target Mintlify's page structure; review them.`
|
|
8448
|
+
});
|
|
8449
|
+
}
|
|
8450
|
+
for (const entry of copiedSite.filter((item) => item.kind === "style")) {
|
|
8451
|
+
warnings.push({
|
|
8452
|
+
code: "unsupported-config",
|
|
8453
|
+
message: `Migration gap: custom stylesheet ${entry.path} is NOT applied. It was copied to public/${entry.path} but no page loads it, Thally cannot load a stylesheet from docs.json, and Mintlify-specific selectors will not match Thally's markup. Port the styles you need by hand into src/app/globals.css.`,
|
|
8454
|
+
source: entry.path
|
|
8455
|
+
});
|
|
8456
|
+
}
|
|
8457
|
+
for (const font of mintlifyFonts) {
|
|
8458
|
+
const copiedPath = font.path ? fontAssetByRelative.get(font.path) : void 0;
|
|
8459
|
+
const copied = copiedPath !== void 0 && assets.some((asset) => asset.path === copiedPath);
|
|
8460
|
+
const detail = font.remote ? `its source ${font.source} is a remote URL and was not downloaded` : !font.path ? `its source ${font.source} is not a safe local .woff/.woff2/.ttf/.otf path inside the docs directory and was not imported` : !copiedPath ? `its source file ${font.source} was not found in the docs directory` : copied ? `its file was copied to public/${copiedPath}` : null;
|
|
8461
|
+
if (detail === null) continue;
|
|
8462
|
+
warnings.push({
|
|
8463
|
+
code: "unsupported-config",
|
|
8464
|
+
message: `Self-hosted font "${font.family}" is not applied: Thally's fonts setting loads Google Fonts by family only; ${detail}. The family name was still mapped, which loads a Google font of that name if one exists.`
|
|
8465
|
+
});
|
|
8466
|
+
}
|
|
8467
|
+
}
|
|
7724
8468
|
if (overBudgetAssets.length > 0) {
|
|
7725
8469
|
warnings.push({
|
|
7726
8470
|
code: "limit-reached",
|
|
@@ -7733,8 +8477,8 @@ function migrateRepository(options) {
|
|
|
7733
8477
|
message: `${lfsPointerAssets.length} asset file${lfsPointerAssets.length === 1 ? " is a Git LFS pointer" : "s are Git LFS pointers"}, not real content, and ${lfsPointerAssets.length === 1 ? "was" : "were"} not copied (Git LFS was skipped during clone because the host has no git-lfs binary): ${listAssetPaths(lfsPointerAssets)}. Install git-lfs and re-run the migration, or copy the real files into public/ manually.`
|
|
7734
8478
|
});
|
|
7735
8479
|
}
|
|
7736
|
-
if (!discoveryBudgetApplied && files.length >=
|
|
7737
|
-
warnings.push({ code: "limit-reached", message: `Stopped scanning after ${
|
|
8480
|
+
if (!discoveryBudgetApplied && files.length >= sourceBudget) {
|
|
8481
|
+
warnings.push({ code: "limit-reached", message: `Stopped scanning after ${sourceBudget} files, so the rest of the repository was not looked at. Run the migration on a smaller part of the repository with --docs-dir.` });
|
|
7738
8482
|
}
|
|
7739
8483
|
if (platform === "docusaurus") {
|
|
7740
8484
|
const projected = projectDocusaurusNavigation({
|
|
@@ -7861,11 +8605,19 @@ function migrateRepository(options) {
|
|
|
7861
8605
|
rewriteApiLinksInPages(pages, operationLinks, prefixLandings, warnings);
|
|
7862
8606
|
}
|
|
7863
8607
|
} else if (platform === "mintlify") {
|
|
8608
|
+
const warnSharedSpec = (path) => {
|
|
8609
|
+
warnings.push({
|
|
8610
|
+
code: "gated-page",
|
|
8611
|
+
message: `OpenAPI spec ${path} is shared with access-restricted pages and is published; it may describe restricted endpoints. Review it before publishing.`,
|
|
8612
|
+
source: path
|
|
8613
|
+
});
|
|
8614
|
+
};
|
|
7864
8615
|
const resolvedSpecs = resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteApiSpecs);
|
|
7865
8616
|
for (const spec of resolvedSpecs) {
|
|
7866
8617
|
if (!assets.some((asset) => asset.path === specAssetPath(spec.filename))) {
|
|
7867
8618
|
assets.push(specAsset(spec.filename, spec.content));
|
|
7868
8619
|
}
|
|
8620
|
+
if (withheldSpecRefs.some((ref) => specRefMatches(ref, spec.sourcePath, spec.filename, true))) warnSharedSpec(spec.sourcePath);
|
|
7869
8621
|
}
|
|
7870
8622
|
if (resolvedSpecs.length > 0) {
|
|
7871
8623
|
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs, warnings);
|
|
@@ -7878,10 +8630,17 @@ function migrateRepository(options) {
|
|
|
7878
8630
|
const fallback = findOpenApi(files);
|
|
7879
8631
|
if (fallback) {
|
|
7880
8632
|
const filename = basename3(fallback.relativePath);
|
|
7881
|
-
|
|
7882
|
-
|
|
8633
|
+
const matches = (ref) => specRefMatches(ref, fallback.relativePath, filename, true);
|
|
8634
|
+
if (withheldSpecRefs.some(matches) && !publishedSpecRefs.some(matches)) {
|
|
8635
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${specAssetPath(filename)}`, content: readFileSync5(fallback.absolutePath) });
|
|
8636
|
+
withheldAssetCount++;
|
|
8637
|
+
} else {
|
|
8638
|
+
if (!assets.some((asset) => asset.path === specAssetPath(filename))) {
|
|
8639
|
+
assets.push(specAsset(filename, readFileSync5(fallback.absolutePath)));
|
|
8640
|
+
}
|
|
8641
|
+
docsConfig = injectOpenApiSpecs(docsConfig, [{ filename }]);
|
|
8642
|
+
if (withheldSpecRefs.some(matches)) warnSharedSpec(fallback.relativePath);
|
|
7883
8643
|
}
|
|
7884
|
-
docsConfig = injectOpenApiSpecs(docsConfig, [{ filename }]);
|
|
7885
8644
|
}
|
|
7886
8645
|
}
|
|
7887
8646
|
}
|
|
@@ -8192,10 +8951,12 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
8192
8951
|
docsConfig = { ...docsConfig, navbar: { ...docsConfig.navbar, logo: null } };
|
|
8193
8952
|
}
|
|
8194
8953
|
if (mintlifyConfig) {
|
|
8195
|
-
const
|
|
8196
|
-
|
|
8197
|
-
if (
|
|
8198
|
-
|
|
8954
|
+
const analytics = projectMintlifyIntegrations(mintlifyConfig);
|
|
8955
|
+
warnings.push(...analytics.warnings);
|
|
8956
|
+
if (analytics.integrations) docsConfig = { ...docsConfig, integrations: analytics.integrations };
|
|
8957
|
+
const appearance = mintlifyAppearance(mintlifyConfig, warnings);
|
|
8958
|
+
if (Object.keys(appearance).length > 0) {
|
|
8959
|
+
docsConfig = { ...docsConfig, appearance: { ...docsConfig.appearance, ...appearance } };
|
|
8199
8960
|
}
|
|
8200
8961
|
const background = mintlifyConfig.background && typeof mintlifyConfig.background === "object" ? mintlifyConfig.background : null;
|
|
8201
8962
|
if (background) {
|
|
@@ -8228,6 +8989,25 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
8228
8989
|
code: "unsupported-config",
|
|
8229
8990
|
message: `Brand asset(s) were referenced but not imported: ${[...new Set(missingBrand)].join(", ")}. Add these files under public/ or update docs.json.`
|
|
8230
8991
|
});
|
|
8992
|
+
if (platform === "mintlify") {
|
|
8993
|
+
docsConfig = { ...docsConfig, markdown: { enabled: true } };
|
|
8994
|
+
if (quarantinedPageCount > 0 || withheldAssetCount > 0 || unreferencedAssetCount > 0) {
|
|
8995
|
+
const assetNote = withheldAssetCount > 0 ? `${withheldAssetCount} file(s) used only by access-restricted pages were kept out of public/ and saved under ${QUARANTINE_DIRECTORY}/assets/. ` : "";
|
|
8996
|
+
const unreferencedReason = [
|
|
8997
|
+
...hasWithheldContent ? ["this site has access-restricted content"] : [],
|
|
8998
|
+
...pagesNotClassified ? ["pages were dropped by the file limit (or the file scan stopped at its cap) and could not all be checked for access restrictions"] : []
|
|
8999
|
+
].join(" and ");
|
|
9000
|
+
const unreferencedNote = unreferencedAssetCount > 0 ? `${unreferencedAssetCount} unreferenced asset(s) were kept out of public/ because ${unreferencedReason}; review ${QUARANTINE_DIRECTORY}/assets/ and copy any that published pages need. ` : "";
|
|
9001
|
+
warnings.push({
|
|
9002
|
+
code: "gated-page",
|
|
9003
|
+
message: (quarantinedPageCount > 0 ? `${quarantinedPageCount} access-restricted page(s) were withheld from the published site and saved under ${QUARANTINE_DIRECTORY}/ (local only: git-ignored, never served or deployed). ` : "") + assetNote + unreferencedNote + "Assets that published pages also use are still copied to public/. Review them before deciding how to publish or protect that content."
|
|
9004
|
+
});
|
|
9005
|
+
}
|
|
9006
|
+
warnings.push({
|
|
9007
|
+
code: "gated-page",
|
|
9008
|
+
message: (sawPublicTrue ? "Some pages set `public: true`, which means the source site used Mintlify authentication and every page WITHOUT it was private. " : "") + "Access control set in the Mintlify dashboard is not visible in the repository. Check the source site's dashboard access settings before publishing: Thally will publish ALL imported pages publicly. Confirm nothing here was meant to stay private before deploying."
|
|
9009
|
+
});
|
|
9010
|
+
}
|
|
8231
9011
|
if (fernRawConfig?.logo && !logoLight && !logo.light) warnings.push({
|
|
8232
9012
|
code: "unsupported-config",
|
|
8233
9013
|
message: "Fern supplied a logo through its global theme without a local asset; add a logo path to docs.json after import."
|
|
@@ -8240,6 +9020,8 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
8240
9020
|
assets,
|
|
8241
9021
|
...remoteApiSpecs.length > 0 ? { remoteApiSpecs } : {},
|
|
8242
9022
|
...componentMigrator ? { componentFiles: componentMigrator.files() } : {},
|
|
9023
|
+
...quarantinedFiles.length > 0 ? { quarantinedFiles } : {},
|
|
9024
|
+
...droppedGatedFiles.length > 0 ? { droppedGatedPages: droppedGatedFiles.length } : {},
|
|
8243
9025
|
docsConfig,
|
|
8244
9026
|
...mintlifyConfig ? {
|
|
8245
9027
|
site: {
|
|
@@ -8290,6 +9072,9 @@ function renderPage(bundle, page) {
|
|
|
8290
9072
|
""
|
|
8291
9073
|
].filter((line) => line !== null).join("\n");
|
|
8292
9074
|
}
|
|
9075
|
+
function isIntegrationsObject(value) {
|
|
9076
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
9077
|
+
}
|
|
8293
9078
|
function mergeMigrationConfig(existing, incoming) {
|
|
8294
9079
|
const tabs = existing.tabs.filter((tab) => tab.tab.toLowerCase() !== "changelog");
|
|
8295
9080
|
const names = new Set(tabs.map((tab) => tab.tab.toLowerCase()));
|
|
@@ -8360,7 +9145,15 @@ function mergeMigrationConfig(existing, incoming) {
|
|
|
8360
9145
|
// Collection presentation belongs to the imported information
|
|
8361
9146
|
// architecture; starter defaults must not coerce source dropdowns to tabs.
|
|
8362
9147
|
navigation: incoming.navigation ?? existing.navigation,
|
|
8363
|
-
...i18n ? { i18n } : {}
|
|
9148
|
+
...i18n ? { i18n } : {},
|
|
9149
|
+
// Existing providers win per provider; imported ones fill the gaps.
|
|
9150
|
+
// A malformed (string/array) existing value must not spread into index keys.
|
|
9151
|
+
...isIntegrationsObject(existing.integrations) || isIntegrationsObject(incoming.integrations) ? {
|
|
9152
|
+
integrations: {
|
|
9153
|
+
...isIntegrationsObject(incoming.integrations) ? incoming.integrations : {},
|
|
9154
|
+
...isIntegrationsObject(existing.integrations) ? existing.integrations : {}
|
|
9155
|
+
}
|
|
9156
|
+
} : {}
|
|
8364
9157
|
};
|
|
8365
9158
|
}
|
|
8366
9159
|
function renderMigrationFiles(bundle, options = {}) {
|
|
@@ -8372,6 +9165,7 @@ function renderMigrationFiles(bundle, options = {}) {
|
|
|
8372
9165
|
})),
|
|
8373
9166
|
...bundle.assets.map((asset) => ({ path: asset.projectRelative ? asset.path : `public/${asset.path}`, content: asset.content })),
|
|
8374
9167
|
...(bundle.componentFiles ?? []).flatMap((file) => file.path === "src/mdx/custom-components.tsx" && options.existingComponentRegistry !== void 0 && typeof file.content === "string" ? mergeComponentRegistry(options.existingComponentRegistry, file.content) : [file]),
|
|
9168
|
+
...bundle.quarantinedFiles ?? [],
|
|
8375
9169
|
{ path: "docs.json", content: `${JSON.stringify(config, null, 2)}
|
|
8376
9170
|
` }
|
|
8377
9171
|
];
|
|
@@ -9350,7 +10144,7 @@ ${fence}
|
|
|
9350
10144
|
}
|
|
9351
10145
|
});
|
|
9352
10146
|
const escape = turndown.escape.bind(turndown);
|
|
9353
|
-
turndown.escape = (
|
|
10147
|
+
turndown.escape = (text2) => escape(text2).replace(/[<{]/g, (match) => `\\${match}`);
|
|
9354
10148
|
const body = turndown.turndown(root.html() ?? "").trim();
|
|
9355
10149
|
if (!title || body.length < 40 && $("article").length === 0) return { page: null, links };
|
|
9356
10150
|
return {
|
|
@@ -9903,6 +10697,11 @@ async function migrateUrl(options) {
|
|
|
9903
10697
|
topLevelNavigation
|
|
9904
10698
|
});
|
|
9905
10699
|
if (sourceMintlifyProjection) warnings.push(...sourceMintlifyProjection.warnings);
|
|
10700
|
+
if (sourceMintlifyConfig) {
|
|
10701
|
+
const analytics = projectMintlifyIntegrations(sourceMintlifyConfig);
|
|
10702
|
+
warnings.push(...analytics.warnings);
|
|
10703
|
+
if (analytics.integrations) docsConfig.integrations = analytics.integrations;
|
|
10704
|
+
}
|
|
9906
10705
|
if (docusaurusRedirects.length > 0) {
|
|
9907
10706
|
docsConfig.redirects = [...new Map(docusaurusRedirects.map((redirect) => [
|
|
9908
10707
|
`${redirect.source}:${redirect.destination}`,
|
|
@@ -10051,8 +10850,8 @@ async function downloadSpec(value, fetcher) {
|
|
|
10051
10850
|
throw new Error("Remote OpenAPI spec redirected too many times.");
|
|
10052
10851
|
}
|
|
10053
10852
|
function parseOpenApi(content, extension) {
|
|
10054
|
-
const
|
|
10055
|
-
const parsed = extension === "yaml" || !
|
|
10853
|
+
const text2 = content.toString("utf8");
|
|
10854
|
+
const parsed = extension === "yaml" || !text2.trimStart().startsWith("{") ? parseYaml6(text2) : JSON.parse(text2);
|
|
10056
10855
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("OpenAPI spec is not an object.");
|
|
10057
10856
|
const document = parsed;
|
|
10058
10857
|
if (typeof document.openapi !== "string" || !/^3\./.test(document.openapi)) {
|
|
@@ -10162,11 +10961,13 @@ export {
|
|
|
10162
10961
|
parseGitHubRepositoryUrl,
|
|
10163
10962
|
parseMarkdownPage,
|
|
10164
10963
|
projectFernNavigation,
|
|
10964
|
+
projectMintlifyIntegrations,
|
|
10165
10965
|
projectMintlifyNavigation,
|
|
10166
10966
|
pruneMissingNavigationPages,
|
|
10167
10967
|
readFernConfig,
|
|
10168
10968
|
readMintlifyConfig,
|
|
10169
10969
|
renderMigrationFiles,
|
|
10970
|
+
validateIntegrations,
|
|
10170
10971
|
validateMigrationUrl
|
|
10171
10972
|
};
|
|
10172
10973
|
//# sourceMappingURL=index.js.map
|