@thallylabs/migrate 0.2.12 → 0.2.17
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 +79 -10
- package/dist/index.d.ts +26 -2
- package/dist/index.js +998 -178
- 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
|
|
|
@@ -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;
|
|
@@ -3235,7 +3318,7 @@ function projectedHref(value) {
|
|
|
3235
3318
|
return href;
|
|
3236
3319
|
}
|
|
3237
3320
|
function labelFor(value, fallback) {
|
|
3238
|
-
for (const key of ["tab", "group", "anchor", "product", "dropdown", "version", "menu", "label", "name", "title"]) {
|
|
3321
|
+
for (const key of ["tab", "group", "anchor", "product", "dropdown", "version", "menu", "item", "label", "name", "title"]) {
|
|
3239
3322
|
if (typeof value[key] === "string" && value[key]) return String(value[key]);
|
|
3240
3323
|
}
|
|
3241
3324
|
return fallback;
|
|
@@ -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]) => [
|
|
@@ -3307,34 +3390,45 @@ function readMintlifyConfig(repositoryRoot) {
|
|
|
3307
3390
|
const raw = readMintlifyJson(configPath, repositoryRoot);
|
|
3308
3391
|
return resolveJsonReferences(raw, configPath, repositoryRoot, /* @__PURE__ */ new Set());
|
|
3309
3392
|
}
|
|
3393
|
+
function specSource(value) {
|
|
3394
|
+
const source = typeof value === "string" ? value : objectValue2(value)?.source;
|
|
3395
|
+
return typeof source === "string" && source.trim() ? source.trim() : void 0;
|
|
3396
|
+
}
|
|
3397
|
+
var hasNavigationChildren = (node) => ["groups", "pages"].some((key) => Array.isArray(node[key]) && node[key].length > 0);
|
|
3310
3398
|
function mintlifyNavigationApiReferences(config) {
|
|
3311
|
-
const navigation =
|
|
3399
|
+
const navigation = objectValue2(config.navigation) ?? config;
|
|
3312
3400
|
const references = [];
|
|
3313
3401
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus", "languages"];
|
|
3314
3402
|
const nestedKeys = ["groups", "pages"];
|
|
3315
|
-
function
|
|
3316
|
-
const node = withFlattenedProductGroups(rawNode);
|
|
3403
|
+
function collect(node, tabLabel, sibling) {
|
|
3317
3404
|
for (const kind of ["openapi", "asyncapi"]) {
|
|
3318
|
-
const value = node[kind];
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
continue;
|
|
3322
|
-
}
|
|
3323
|
-
const object = objectValue(value);
|
|
3324
|
-
if (object && typeof object.source === "string" && object.source.trim()) {
|
|
3405
|
+
const value = specSource(node[kind]);
|
|
3406
|
+
const directory = objectValue2(node[kind])?.directory;
|
|
3407
|
+
if (value) {
|
|
3325
3408
|
references.push({
|
|
3326
|
-
value
|
|
3409
|
+
value,
|
|
3327
3410
|
kind,
|
|
3328
3411
|
tabLabel,
|
|
3329
|
-
...
|
|
3412
|
+
...sibling,
|
|
3413
|
+
...typeof directory === "string" && directory.trim() ? { directory: directory.trim() } : {}
|
|
3330
3414
|
});
|
|
3331
3415
|
}
|
|
3332
3416
|
}
|
|
3417
|
+
}
|
|
3418
|
+
const siblingSources = /* @__PURE__ */ new Map();
|
|
3419
|
+
function uniqueSiblingLabel(label, source) {
|
|
3420
|
+
let candidate = label;
|
|
3421
|
+
for (let n = 2; siblingSources.has(candidate) && siblingSources.get(candidate) !== source; n++) candidate = `${label} (${n})`;
|
|
3422
|
+
siblingSources.set(candidate, source);
|
|
3423
|
+
return candidate;
|
|
3424
|
+
}
|
|
3425
|
+
function visitChildren(rawNode, tabLabel) {
|
|
3426
|
+
const node = withFlattenedProductGroups(rawNode);
|
|
3333
3427
|
for (const key of containerKeys) {
|
|
3334
3428
|
const entries = node[key];
|
|
3335
3429
|
if (!Array.isArray(entries)) continue;
|
|
3336
3430
|
for (const entry of entries) {
|
|
3337
|
-
const object =
|
|
3431
|
+
const object = objectValue2(entry);
|
|
3338
3432
|
if (!object) continue;
|
|
3339
3433
|
visit(object, labelFor(object, tabLabel ?? "Documentation"));
|
|
3340
3434
|
}
|
|
@@ -3344,14 +3438,49 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
3344
3438
|
if (!Array.isArray(entries)) continue;
|
|
3345
3439
|
for (const entry of entries) {
|
|
3346
3440
|
if (typeof entry === "string") continue;
|
|
3347
|
-
const object =
|
|
3441
|
+
const object = objectValue2(entry);
|
|
3348
3442
|
if (object) visit(object, tabLabel);
|
|
3349
3443
|
}
|
|
3350
3444
|
}
|
|
3445
|
+
if (Array.isArray(node.menu)) {
|
|
3446
|
+
const tab = tabLabel ?? "Documentation";
|
|
3447
|
+
const items = node.menu.flatMap((entry) => {
|
|
3448
|
+
const object = objectValue2(entry);
|
|
3449
|
+
return object ? [object] : [];
|
|
3450
|
+
});
|
|
3451
|
+
const apiItems = items.filter((item) => specSource(item.openapi) || specSource(item.asyncapi));
|
|
3452
|
+
const soleContent = apiItems.length === 1 && !hasNavigationChildren(node) && !items.some(hasNavigationChildren);
|
|
3453
|
+
items.forEach((item, index) => {
|
|
3454
|
+
if (soleContent && apiItems[0] === item) collect(item, tab);
|
|
3455
|
+
else if (apiItems.includes(item)) {
|
|
3456
|
+
const label = uniqueSiblingLabel(
|
|
3457
|
+
`${tab}: ${labelFor(item, `Menu item ${index + 1}`)}`,
|
|
3458
|
+
`${specSource(item.openapi) ?? ""}|${specSource(item.asyncapi) ?? ""}`
|
|
3459
|
+
);
|
|
3460
|
+
const icon = iconName(item.icon);
|
|
3461
|
+
collect(item, label, { parentTab: tab, ...icon ? { icon } : {}, ...item.hidden === true ? { hidden: true } : {} });
|
|
3462
|
+
}
|
|
3463
|
+
visitChildren(item, tabLabel);
|
|
3464
|
+
});
|
|
3465
|
+
}
|
|
3466
|
+
}
|
|
3467
|
+
function visit(node, tabLabel) {
|
|
3468
|
+
collect(node, tabLabel);
|
|
3469
|
+
visitChildren(node, tabLabel);
|
|
3351
3470
|
}
|
|
3352
3471
|
visit(navigation, void 0);
|
|
3353
3472
|
return references;
|
|
3354
3473
|
}
|
|
3474
|
+
function insertApiTab(tabs, tab, parentTab) {
|
|
3475
|
+
let index = -1;
|
|
3476
|
+
if (parentTab) {
|
|
3477
|
+
tabs.forEach((candidate, position) => {
|
|
3478
|
+
if (candidate.tab === parentTab || candidate.tab.startsWith(`${parentTab}: `)) index = position;
|
|
3479
|
+
});
|
|
3480
|
+
}
|
|
3481
|
+
tabs.splice(index === -1 ? tabs.length : index + 1, 0, tab);
|
|
3482
|
+
return tabs;
|
|
3483
|
+
}
|
|
3355
3484
|
function normalizePageRef(value, pathPrefix = "") {
|
|
3356
3485
|
if (/^(?:https?:)?\/\//i.test(value) || value.startsWith("#")) return null;
|
|
3357
3486
|
let ref = localReference(value).split("?", 1)[0].replace(/^\/+/, "");
|
|
@@ -3362,6 +3491,23 @@ function normalizePageRef(value, pathPrefix = "") {
|
|
|
3362
3491
|
ref = ref.replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
3363
3492
|
return pageIdFromReference(ref, true);
|
|
3364
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
|
+
}
|
|
3365
3511
|
function warnOnce(context, key, message) {
|
|
3366
3512
|
if (context.warningKeys.has(key)) return;
|
|
3367
3513
|
context.warningKeys.add(key);
|
|
@@ -3369,7 +3515,7 @@ function warnOnce(context, key, message) {
|
|
|
3369
3515
|
}
|
|
3370
3516
|
function iconName(value) {
|
|
3371
3517
|
if (typeof value === "string" && value) return value;
|
|
3372
|
-
const icon =
|
|
3518
|
+
const icon = objectValue2(value);
|
|
3373
3519
|
return typeof icon?.name === "string" && icon.name ? icon.name : void 0;
|
|
3374
3520
|
}
|
|
3375
3521
|
function containerPresentation(value) {
|
|
@@ -3379,6 +3525,10 @@ function containerPresentation(value) {
|
|
|
3379
3525
|
};
|
|
3380
3526
|
}
|
|
3381
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
|
+
}
|
|
3382
3532
|
const localizedValue = context.locale ? mintlifyLocalizedReference(value, context.locale, context.defaultPageIds) : value;
|
|
3383
3533
|
const navigationId = normalizePageRef(localizedValue, context.pathPrefix);
|
|
3384
3534
|
if (!navigationId) return null;
|
|
@@ -3402,13 +3552,24 @@ function convertPage(value, context) {
|
|
|
3402
3552
|
}
|
|
3403
3553
|
return registerReference(value, context);
|
|
3404
3554
|
}
|
|
3405
|
-
const object =
|
|
3555
|
+
const object = objectValue2(value);
|
|
3406
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) {
|
|
3407
3567
|
if (typeof object.page === "string") return registerReference(object.page, context);
|
|
3408
3568
|
const href = projectedHref(object.href);
|
|
3409
3569
|
if (href && !object.pages && !object.groups) {
|
|
3410
3570
|
const page = registerReference(href, context);
|
|
3411
3571
|
if (page) return page;
|
|
3572
|
+
if (context.gateReason) return null;
|
|
3412
3573
|
warnOnce(
|
|
3413
3574
|
context,
|
|
3414
3575
|
"external-page-link",
|
|
@@ -3437,6 +3598,65 @@ function convertPage(value, context) {
|
|
|
3437
3598
|
}
|
|
3438
3599
|
return null;
|
|
3439
3600
|
}
|
|
3601
|
+
var KNOWN_NAVIGATION_KEYS = /* @__PURE__ */ new Set([
|
|
3602
|
+
"tabs",
|
|
3603
|
+
"anchors",
|
|
3604
|
+
"products",
|
|
3605
|
+
"dropdowns",
|
|
3606
|
+
"versions",
|
|
3607
|
+
"menus",
|
|
3608
|
+
"languages",
|
|
3609
|
+
"productGroups",
|
|
3610
|
+
"groups",
|
|
3611
|
+
"pages",
|
|
3612
|
+
"menu",
|
|
3613
|
+
"global"
|
|
3614
|
+
]);
|
|
3615
|
+
function convertMenuItems(items, context) {
|
|
3616
|
+
return items.flatMap((value, index) => {
|
|
3617
|
+
const item = objectValue2(value);
|
|
3618
|
+
if (!item) return [];
|
|
3619
|
+
const label = labelFor(item, `Menu item ${index + 1}`);
|
|
3620
|
+
if (typeof item.description === "string" && item.description.trim()) {
|
|
3621
|
+
warnOnce(
|
|
3622
|
+
context,
|
|
3623
|
+
`menu-item-description:${label}`,
|
|
3624
|
+
`Mintlify menu item "${label}" has a description, which Thally groups cannot display; it was omitted.`
|
|
3625
|
+
);
|
|
3626
|
+
}
|
|
3627
|
+
const hasUnsourcedSpec = Boolean(item.openapi || item.asyncapi) && !specSource(item.openapi) && !specSource(item.asyncapi);
|
|
3628
|
+
if (hasUnsourcedSpec) {
|
|
3629
|
+
warnOnce(
|
|
3630
|
+
context,
|
|
3631
|
+
`menu-item-spec:${label}`,
|
|
3632
|
+
`Mintlify menu item "${label}" has an openapi/asyncapi value without a "source", so its API reference was not migrated.`
|
|
3633
|
+
);
|
|
3634
|
+
}
|
|
3635
|
+
const children = convertNavigationValues([
|
|
3636
|
+
...Array.isArray(item.groups) ? item.groups : [],
|
|
3637
|
+
...Array.isArray(item.pages) ? item.pages : []
|
|
3638
|
+
], context);
|
|
3639
|
+
if (typeof item.href === "string" && item.href) {
|
|
3640
|
+
warnOnce(
|
|
3641
|
+
context,
|
|
3642
|
+
`menu-item-href:${label}`,
|
|
3643
|
+
`Mintlify menu item "${label}" links to ${item.href}, which cannot be represented as a Thally group; the link was omitted.`
|
|
3644
|
+
);
|
|
3645
|
+
}
|
|
3646
|
+
if (children.length === 0) {
|
|
3647
|
+
if (!specSource(item.openapi) && !specSource(item.asyncapi) && !hasUnsourcedSpec) {
|
|
3648
|
+
warnOnce(context, `menu-item-empty:${label}`, `Mintlify menu item "${label}" has no projectable pages and was omitted.`);
|
|
3649
|
+
}
|
|
3650
|
+
return [];
|
|
3651
|
+
}
|
|
3652
|
+
return [{
|
|
3653
|
+
group: label,
|
|
3654
|
+
...iconName(item.icon) ? { icon: iconName(item.icon) } : {},
|
|
3655
|
+
...item.hidden === true ? { hidden: true } : {},
|
|
3656
|
+
pages: children
|
|
3657
|
+
}];
|
|
3658
|
+
});
|
|
3659
|
+
}
|
|
3440
3660
|
function convertNavigationValues(values, context) {
|
|
3441
3661
|
return values.flatMap((value) => {
|
|
3442
3662
|
const page = convertPage(value, context);
|
|
@@ -3444,22 +3664,29 @@ function convertNavigationValues(values, context) {
|
|
|
3444
3664
|
});
|
|
3445
3665
|
}
|
|
3446
3666
|
function convertContainerToTabs(containerValue, context, fallbackTab, trace, depth = 0) {
|
|
3447
|
-
const rawContainer =
|
|
3667
|
+
const rawContainer = objectValue2(containerValue);
|
|
3448
3668
|
if (!rawContainer) return [];
|
|
3449
3669
|
const container = withFlattenedProductGroups(rawContainer);
|
|
3450
3670
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus"];
|
|
3671
|
+
for (const key of Object.keys(container)) {
|
|
3672
|
+
if (KNOWN_NAVIGATION_KEYS.has(key)) continue;
|
|
3673
|
+
const entries = container[key];
|
|
3674
|
+
if (Array.isArray(entries) && entries.some((entry) => objectValue2(entry))) {
|
|
3675
|
+
warnOnce(context, `unknown-container:${key}`, `Mintlify navigation key "${key}" is not supported and its contents were not projected.`);
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3451
3678
|
for (const key of containerKeys) {
|
|
3452
3679
|
if (!Array.isArray(container[key])) continue;
|
|
3453
3680
|
const entries = [...container[key]];
|
|
3454
3681
|
if (key === "versions") {
|
|
3455
|
-
entries.sort((left, right) => Number(
|
|
3682
|
+
entries.sort((left, right) => Number(objectValue2(right)?.default === true) - Number(objectValue2(left)?.default === true));
|
|
3456
3683
|
}
|
|
3457
3684
|
const tabs = entries.flatMap((value, index) => {
|
|
3458
|
-
const object =
|
|
3685
|
+
const object = objectValue2(value);
|
|
3459
3686
|
if (!object) return [];
|
|
3460
3687
|
const tab = labelFor(object, `${fallbackTab} ${index + 1}`);
|
|
3461
3688
|
const href = projectedHref(object.href);
|
|
3462
|
-
const hasNestedContainers = containerKeys.some((containerKey) => Array.isArray(object[containerKey]));
|
|
3689
|
+
const hasNestedContainers = [...containerKeys, "menu"].some((containerKey) => Array.isArray(object[containerKey]));
|
|
3463
3690
|
if (href && !object.pages && !object.groups && !hasNestedContainers) {
|
|
3464
3691
|
return [{
|
|
3465
3692
|
tab,
|
|
@@ -3468,7 +3695,15 @@ function convertContainerToTabs(containerValue, context, fallbackTab, trace, dep
|
|
|
3468
3695
|
...object.hidden === true ? { hidden: true } : {}
|
|
3469
3696
|
}];
|
|
3470
3697
|
}
|
|
3471
|
-
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
|
+
}
|
|
3472
3707
|
if (nested.length > 0) {
|
|
3473
3708
|
if (nested.length === 1) {
|
|
3474
3709
|
return [{
|
|
@@ -3491,6 +3726,11 @@ function convertContainerToTabs(containerValue, context, fallbackTab, trace, dep
|
|
|
3491
3726
|
return [];
|
|
3492
3727
|
});
|
|
3493
3728
|
if (tabs.length > 0) {
|
|
3729
|
+
for (const sibling of ["groups", "pages", "menu"]) {
|
|
3730
|
+
if (Array.isArray(container[sibling]) && container[sibling].length > 0) {
|
|
3731
|
+
warnOnce(context, `container-sibling:${key}:${sibling}`, `Mintlify "${sibling}" next to "${key}" was not projected; only the "${key}" containers were kept.`);
|
|
3732
|
+
}
|
|
3733
|
+
}
|
|
3494
3734
|
if (depth === 0 && trace) trace.rootContainerKind = key;
|
|
3495
3735
|
return tabs;
|
|
3496
3736
|
}
|
|
@@ -3503,7 +3743,10 @@ function convertContainerToTabs(containerValue, context, fallbackTab, trace, dep
|
|
|
3503
3743
|
...rawGroups,
|
|
3504
3744
|
...rawPages
|
|
3505
3745
|
];
|
|
3506
|
-
const children =
|
|
3746
|
+
const children = [
|
|
3747
|
+
...convertNavigationValues(values, context),
|
|
3748
|
+
...Array.isArray(container.menu) ? convertMenuItems(container.menu, context) : []
|
|
3749
|
+
];
|
|
3507
3750
|
return children.length > 0 ? [{
|
|
3508
3751
|
tab: fallbackTab,
|
|
3509
3752
|
...hasRootNodes ? { pages: children } : { groups: children },
|
|
@@ -3519,17 +3762,17 @@ function projectedTheme(value) {
|
|
|
3519
3762
|
return typeof value === "string" ? "default" : void 0;
|
|
3520
3763
|
}
|
|
3521
3764
|
function projectedNavbar(value) {
|
|
3522
|
-
const navbar =
|
|
3765
|
+
const navbar = objectValue2(value);
|
|
3523
3766
|
if (!navbar) return void 0;
|
|
3524
3767
|
const links = Array.isArray(navbar.links) ? navbar.links.flatMap((entry) => {
|
|
3525
|
-
const link =
|
|
3768
|
+
const link = objectValue2(entry);
|
|
3526
3769
|
const href = projectedHref(link?.href);
|
|
3527
3770
|
if (!link || !href) return [];
|
|
3528
3771
|
const label = typeof link.label === "string" ? link.label : typeof link.name === "string" ? link.name : "";
|
|
3529
3772
|
if (!label) return [];
|
|
3530
3773
|
return [{ label, href, ...link.type === "github" ? { type: "github" } : {} }];
|
|
3531
3774
|
}) : [];
|
|
3532
|
-
const primaryValue =
|
|
3775
|
+
const primaryValue = objectValue2(navbar.primary);
|
|
3533
3776
|
const primaryHref = projectedHref(primaryValue?.href);
|
|
3534
3777
|
const primary = primaryValue && primaryHref ? {
|
|
3535
3778
|
label: typeof primaryValue.label === "string" ? primaryValue.label : primaryValue.type === "github" ? "GitHub" : "Get started",
|
|
@@ -3537,8 +3780,22 @@ function projectedNavbar(value) {
|
|
|
3537
3780
|
} : void 0;
|
|
3538
3781
|
return links.length > 0 || primary ? { ...links.length > 0 ? { links } : {}, ...primary ? { primary } : {} } : void 0;
|
|
3539
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
|
+
}
|
|
3540
3797
|
function projectedGlobalNavigationLinks(value) {
|
|
3541
|
-
const global =
|
|
3798
|
+
const global = objectValue2(value);
|
|
3542
3799
|
if (!global) return [];
|
|
3543
3800
|
const links = [];
|
|
3544
3801
|
const visit = (entry) => {
|
|
@@ -3546,14 +3803,14 @@ function projectedGlobalNavigationLinks(value) {
|
|
|
3546
3803
|
entry.forEach(visit);
|
|
3547
3804
|
return;
|
|
3548
3805
|
}
|
|
3549
|
-
const item =
|
|
3806
|
+
const item = objectValue2(entry);
|
|
3550
3807
|
if (!item) return;
|
|
3551
3808
|
const href = projectedHref(item.href);
|
|
3552
3809
|
if (href) {
|
|
3553
3810
|
const label = labelFor(item, "");
|
|
3554
3811
|
if (label) links.push({ label, href });
|
|
3555
3812
|
}
|
|
3556
|
-
for (const key of ["tabs", "anchors", "dropdowns", "products", "versions", "languages", "menus"]) {
|
|
3813
|
+
for (const key of ["tabs", "anchors", "dropdowns", "products", "versions", "languages", "menus", "menu"]) {
|
|
3557
3814
|
if (Array.isArray(item[key])) visit(item[key]);
|
|
3558
3815
|
}
|
|
3559
3816
|
};
|
|
@@ -3561,19 +3818,19 @@ function projectedGlobalNavigationLinks(value) {
|
|
|
3561
3818
|
return links;
|
|
3562
3819
|
}
|
|
3563
3820
|
function projectedFooter(value) {
|
|
3564
|
-
const footer =
|
|
3821
|
+
const footer = objectValue2(value);
|
|
3565
3822
|
if (!footer) return void 0;
|
|
3566
|
-
const socials =
|
|
3823
|
+
const socials = objectValue2(footer.socials);
|
|
3567
3824
|
const socialLinks = socials ? Object.fromEntries(Object.entries(socials).flatMap(([key, value2]) => {
|
|
3568
3825
|
const href = projectedHref(value2);
|
|
3569
3826
|
return href ? [[key, href]] : [];
|
|
3570
3827
|
})) : void 0;
|
|
3571
3828
|
const links = Array.isArray(footer.links) ? footer.links.flatMap((entry) => {
|
|
3572
|
-
const column =
|
|
3829
|
+
const column = objectValue2(entry);
|
|
3573
3830
|
if (!column || !Array.isArray(column.items)) return [];
|
|
3574
3831
|
const heading = typeof column.heading === "string" ? column.heading : typeof column.header === "string" ? column.header : "";
|
|
3575
3832
|
const items = column.items.flatMap((item) => {
|
|
3576
|
-
const link =
|
|
3833
|
+
const link = objectValue2(item);
|
|
3577
3834
|
const href = projectedHref(link?.href);
|
|
3578
3835
|
return link && typeof link.label === "string" && href ? [{ label: link.label, href }] : [];
|
|
3579
3836
|
});
|
|
@@ -3583,26 +3840,45 @@ function projectedFooter(value) {
|
|
|
3583
3840
|
}
|
|
3584
3841
|
function projectedFont(value) {
|
|
3585
3842
|
if (typeof value === "string" && value) return { family: value };
|
|
3586
|
-
const font =
|
|
3843
|
+
const font = objectValue2(value);
|
|
3587
3844
|
if (!font) return void 0;
|
|
3588
3845
|
const family = typeof font.family === "string" ? font.family : typeof font.name === "string" ? font.name : void 0;
|
|
3589
3846
|
if (!family) return void 0;
|
|
3590
3847
|
const weight = Array.isArray(font.weight) ? font.weight.map(String) : Array.isArray(font.weights) ? font.weights.map(String) : void 0;
|
|
3591
3848
|
return { family, ...weight && weight.length > 0 ? { weight } : {} };
|
|
3592
3849
|
}
|
|
3593
|
-
function projectedCompatibleConfig(config) {
|
|
3594
|
-
const banner =
|
|
3595
|
-
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;
|
|
3596
3853
|
const bannerType = banner && ["info", "warning", "critical"].includes(String(banner.type ?? banner.variant)) ? String(banner.type ?? banner.variant) : void 0;
|
|
3597
|
-
const bannerColor =
|
|
3598
|
-
const bodyFont = projectedFont(
|
|
3599
|
-
const headingFont = projectedFont(
|
|
3600
|
-
const feedback =
|
|
3601
|
-
const seo =
|
|
3602
|
-
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;
|
|
3603
3860
|
const projectedIconLibrary = ["lucide", "fontawesome", "tabler"].includes(String(iconLibrary)) ? String(iconLibrary) : void 0;
|
|
3604
|
-
const
|
|
3605
|
-
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);
|
|
3606
3882
|
const navbarLinks = [...new Map(
|
|
3607
3883
|
[...navbar?.links ?? [], ...globalLinks].map((link) => [`${link.label}:${link.href}`, link])
|
|
3608
3884
|
).values()];
|
|
@@ -3669,11 +3945,11 @@ function isRedirectPathSafe(rawSource, rawDestination) {
|
|
|
3669
3945
|
return true;
|
|
3670
3946
|
}
|
|
3671
3947
|
function mintlifyDefaultVersionPrefixes(config) {
|
|
3672
|
-
const navigation =
|
|
3948
|
+
const navigation = objectValue2(config.navigation) ?? config;
|
|
3673
3949
|
const prefixes = /* @__PURE__ */ new Set();
|
|
3674
3950
|
function collect(container) {
|
|
3675
3951
|
if (Array.isArray(container.versions)) {
|
|
3676
|
-
const entries = container.versions.map((value) =>
|
|
3952
|
+
const entries = container.versions.map((value) => objectValue2(value)).filter((value) => value !== null);
|
|
3677
3953
|
const defaultEntry = entries.find((entry) => entry.default === true) ?? entries[0];
|
|
3678
3954
|
if (defaultEntry && typeof defaultEntry.version === "string" && defaultEntry.version.trim()) {
|
|
3679
3955
|
prefixes.add(defaultEntry.version.trim());
|
|
@@ -3681,7 +3957,7 @@ function mintlifyDefaultVersionPrefixes(config) {
|
|
|
3681
3957
|
}
|
|
3682
3958
|
if (Array.isArray(container.languages)) {
|
|
3683
3959
|
for (const value of container.languages) {
|
|
3684
|
-
const language =
|
|
3960
|
+
const language = objectValue2(value);
|
|
3685
3961
|
if (language) collect(language);
|
|
3686
3962
|
}
|
|
3687
3963
|
}
|
|
@@ -3690,18 +3966,18 @@ function mintlifyDefaultVersionPrefixes(config) {
|
|
|
3690
3966
|
return prefixes;
|
|
3691
3967
|
}
|
|
3692
3968
|
function mintlifyAllVersionPrefixes(config) {
|
|
3693
|
-
const navigation =
|
|
3969
|
+
const navigation = objectValue2(config.navigation) ?? config;
|
|
3694
3970
|
const prefixes = /* @__PURE__ */ new Set();
|
|
3695
3971
|
function collect(container) {
|
|
3696
3972
|
if (Array.isArray(container.versions)) {
|
|
3697
3973
|
for (const value of container.versions) {
|
|
3698
|
-
const entry =
|
|
3974
|
+
const entry = objectValue2(value);
|
|
3699
3975
|
if (entry && typeof entry.version === "string" && entry.version.trim()) prefixes.add(entry.version.trim());
|
|
3700
3976
|
}
|
|
3701
3977
|
}
|
|
3702
3978
|
if (Array.isArray(container.languages)) {
|
|
3703
3979
|
for (const value of container.languages) {
|
|
3704
|
-
const language =
|
|
3980
|
+
const language = objectValue2(value);
|
|
3705
3981
|
if (language) collect(language);
|
|
3706
3982
|
}
|
|
3707
3983
|
}
|
|
@@ -3714,9 +3990,10 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3714
3990
|
const references = [];
|
|
3715
3991
|
const seenReferences = /* @__PURE__ */ new Set();
|
|
3716
3992
|
const warningKeys = /* @__PURE__ */ new Set();
|
|
3717
|
-
const
|
|
3993
|
+
const gated = [];
|
|
3994
|
+
const navigation = objectValue2(config.navigation) ?? config;
|
|
3718
3995
|
const languages = Array.isArray(navigation.languages) ? navigation.languages.flatMap((value) => {
|
|
3719
|
-
const language =
|
|
3996
|
+
const language = objectValue2(value);
|
|
3720
3997
|
if (!language) return [];
|
|
3721
3998
|
try {
|
|
3722
3999
|
const code = Intl.getCanonicalLocales(String(language.language ?? language.locale ?? "en"))[0];
|
|
@@ -3753,7 +4030,8 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3753
4030
|
references,
|
|
3754
4031
|
seenReferences,
|
|
3755
4032
|
warnings,
|
|
3756
|
-
warningKeys
|
|
4033
|
+
warningKeys,
|
|
4034
|
+
gated
|
|
3757
4035
|
};
|
|
3758
4036
|
const languageTabs = convertContainerToTabs(
|
|
3759
4037
|
language,
|
|
@@ -3770,7 +4048,7 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3770
4048
|
}
|
|
3771
4049
|
if (Object.keys(localizedNavigation).length > 0) i18n.navigation = localizedNavigation;
|
|
3772
4050
|
} else {
|
|
3773
|
-
const context = { references, seenReferences, warnings, warningKeys, pathPrefix: options.pathPrefix };
|
|
4051
|
+
const context = { references, seenReferences, warnings, warningKeys, gated, pathPrefix: options.pathPrefix };
|
|
3774
4052
|
tabs = convertContainerToTabs(navigation, context, "Documentation", projectionTrace);
|
|
3775
4053
|
if (tabs.length === 0 && Array.isArray(config.navigation)) {
|
|
3776
4054
|
const children = convertNavigationValues(config.navigation, context);
|
|
@@ -3790,7 +4068,7 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3790
4068
|
});
|
|
3791
4069
|
}
|
|
3792
4070
|
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
3793
|
-
const redirect =
|
|
4071
|
+
const redirect = objectValue2(value);
|
|
3794
4072
|
if (!redirect || typeof redirect.source !== "string" || typeof redirect.destination !== "string") return [];
|
|
3795
4073
|
const rawSource = redirect.source.trim();
|
|
3796
4074
|
const rawDestination = redirect.destination.trim();
|
|
@@ -3809,15 +4087,17 @@ function projectMintlifyNavigation(config, options = {}) {
|
|
|
3809
4087
|
...typeof redirect.permanent === "boolean" ? { permanent: redirect.permanent } : {}
|
|
3810
4088
|
}];
|
|
3811
4089
|
}) : [];
|
|
4090
|
+
collectGatedReferences(navigation, void 0, options.pathPrefix, gated);
|
|
3812
4091
|
return {
|
|
3813
4092
|
docsConfig: {
|
|
3814
4093
|
tabs,
|
|
3815
4094
|
...projectionTrace.rootContainerKind === "dropdowns" ? { navigation: { display: "dropdown" } } : {},
|
|
3816
|
-
...projectedCompatibleConfig(config),
|
|
4095
|
+
...projectedCompatibleConfig(config, warnings),
|
|
3817
4096
|
...i18n ? { i18n } : {},
|
|
3818
4097
|
...redirects.length > 0 ? { redirects } : {}
|
|
3819
4098
|
},
|
|
3820
4099
|
pageReferences: references,
|
|
4100
|
+
gatedReferences: gated,
|
|
3821
4101
|
warnings
|
|
3822
4102
|
};
|
|
3823
4103
|
}
|
|
@@ -3923,7 +4203,7 @@ function buildNavigationFromPages(pages, options = {}) {
|
|
|
3923
4203
|
return { tabs: [{ tab: "Documentation", groups }] };
|
|
3924
4204
|
}
|
|
3925
4205
|
function isDocumentationExtension(filename) {
|
|
3926
|
-
return [".md", ".mdx", ".rst", ".txt"].includes(
|
|
4206
|
+
return [".md", ".mdx", ".rst", ".txt"].includes(extname3(filename).toLowerCase());
|
|
3927
4207
|
}
|
|
3928
4208
|
function pruneMissingNavigationPages(config, availableIds) {
|
|
3929
4209
|
const pruneNodes = (nodes) => {
|
|
@@ -3963,7 +4243,7 @@ var SIDEBAR_FILENAMES = [
|
|
|
3963
4243
|
"sidebars.mjs",
|
|
3964
4244
|
"sidebars.ts"
|
|
3965
4245
|
];
|
|
3966
|
-
function
|
|
4246
|
+
function objectValue3(value) {
|
|
3967
4247
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
3968
4248
|
}
|
|
3969
4249
|
function stripNumberPrefix(value) {
|
|
@@ -4285,7 +4565,7 @@ function parseStaticSidebarModule(source) {
|
|
|
4285
4565
|
literal
|
|
4286
4566
|
);
|
|
4287
4567
|
const parsed = JSON5.parse(substituted);
|
|
4288
|
-
const object =
|
|
4568
|
+
const object = objectValue3(parsed);
|
|
4289
4569
|
if (!object) continue;
|
|
4290
4570
|
const bindingName = candidate[0].match(/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)/)?.[1];
|
|
4291
4571
|
if (bindingName) {
|
|
@@ -4390,7 +4670,7 @@ function readDocusaurusSidebars(repositoryRoot, versionedSidebarPath) {
|
|
|
4390
4670
|
if (!sourcePath) return null;
|
|
4391
4671
|
const absolutePath = resolveWithin(repositoryRoot, sourcePath);
|
|
4392
4672
|
const source = readBoundedText(absolutePath);
|
|
4393
|
-
const parsed =
|
|
4673
|
+
const parsed = extname4(sourcePath).toLowerCase() === ".json" ? objectValue3(JSON5.parse(source)) : parseStaticSidebarModule(source);
|
|
4394
4674
|
if (!parsed) throw new Error("Docusaurus sidebar config must export an object.");
|
|
4395
4675
|
return { config: parsed, sourcePath };
|
|
4396
4676
|
}
|
|
@@ -4416,7 +4696,7 @@ function redirectEntriesFromObjectLiteralText(objectLiteral) {
|
|
|
4416
4696
|
if (!Array.isArray(parsed)) return [];
|
|
4417
4697
|
const entries = [];
|
|
4418
4698
|
for (const raw of parsed) {
|
|
4419
|
-
const entry =
|
|
4699
|
+
const entry = objectValue3(raw);
|
|
4420
4700
|
if (!entry || typeof entry.to !== "string") continue;
|
|
4421
4701
|
const froms = Array.isArray(entry.from) ? entry.from : [entry.from];
|
|
4422
4702
|
for (const from of froms) {
|
|
@@ -4468,7 +4748,7 @@ function readCategoryMetadata(contentRoot, directory) {
|
|
|
4468
4748
|
try {
|
|
4469
4749
|
const raw = readBoundedText(path);
|
|
4470
4750
|
const parsed = filename.endsWith(".json") ? JSON5.parse(raw) : parseYaml2(raw);
|
|
4471
|
-
const object =
|
|
4751
|
+
const object = objectValue3(parsed);
|
|
4472
4752
|
if (!object) return {};
|
|
4473
4753
|
return {
|
|
4474
4754
|
...typeof object.label === "string" ? { label: object.label } : {},
|
|
@@ -4502,7 +4782,7 @@ function registerDoc(docId, context) {
|
|
|
4502
4782
|
return descriptor.navigationId;
|
|
4503
4783
|
}
|
|
4504
4784
|
function generatedIndexPage(label, linkValue, context) {
|
|
4505
|
-
const link =
|
|
4785
|
+
const link = objectValue3(linkValue);
|
|
4506
4786
|
if (!link) return null;
|
|
4507
4787
|
if (link.type === "doc" && typeof link.id === "string") return registerDoc(link.id, context);
|
|
4508
4788
|
if (link.type !== "generated-index") return null;
|
|
@@ -4769,7 +5049,7 @@ function convertItems(value, context) {
|
|
|
4769
5049
|
return page ? [page] : [];
|
|
4770
5050
|
}
|
|
4771
5051
|
if (Array.isArray(value)) return value.flatMap((entry) => convertItems(entry, context));
|
|
4772
|
-
const object =
|
|
5052
|
+
const object = objectValue3(value);
|
|
4773
5053
|
if (!object) return [];
|
|
4774
5054
|
if (object.type === "doc" || object.type === "ref") {
|
|
4775
5055
|
const page = typeof object.id === "string" ? registerDoc(object.id, context) : null;
|
|
@@ -4835,7 +5115,7 @@ function projectDocusaurusNavigation(input) {
|
|
|
4835
5115
|
|
|
4836
5116
|
// src/fern.ts
|
|
4837
5117
|
import { existsSync as existsSync4, lstatSync as lstatSync4, readFileSync as readFileSync4, readdirSync } from "fs";
|
|
4838
|
-
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";
|
|
4839
5119
|
import { load } from "cheerio";
|
|
4840
5120
|
import { parse as parseYaml3 } from "yaml";
|
|
4841
5121
|
var MAX_CONFIG_BYTES2 = 2e7;
|
|
@@ -4865,7 +5145,7 @@ function announcementMarkdown(message) {
|
|
|
4865
5145
|
for (const [marker, link] of links) text = text.replaceAll(marker, link);
|
|
4866
5146
|
return text.trim();
|
|
4867
5147
|
}
|
|
4868
|
-
function
|
|
5148
|
+
function objectValue4(value) {
|
|
4869
5149
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
4870
5150
|
}
|
|
4871
5151
|
function titleCase3(value) {
|
|
@@ -4881,7 +5161,7 @@ function readFernConfig(fernRoot) {
|
|
|
4881
5161
|
const sourcePath = "docs.yml";
|
|
4882
5162
|
const path = resolveWithin(fernRoot, sourcePath);
|
|
4883
5163
|
if (!existsSync4(path) || !lstatSync4(path).isFile()) return null;
|
|
4884
|
-
const config =
|
|
5164
|
+
const config = objectValue4(readBoundedYaml(path));
|
|
4885
5165
|
if (!config) throw new Error("fern/docs.yml must contain an object.");
|
|
4886
5166
|
return { config, sourcePath };
|
|
4887
5167
|
}
|
|
@@ -4988,8 +5268,8 @@ function registerFolder(object, parentSegments, context) {
|
|
|
4988
5268
|
if (entry.isDirectory()) {
|
|
4989
5269
|
const nested = visit(path, [...routeSegments, fernBasicSlug(entry.name)], depth + 1);
|
|
4990
5270
|
if (nested.length) pages2.push({ group: titleCase3(entry.name), pages: nested });
|
|
4991
|
-
} else if (entry.isFile() && [".md", ".mdx"].includes(
|
|
4992
|
-
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));
|
|
4993
5273
|
const pageSegment = stem.toLowerCase() === "index" ? null : fernBasicSlug(stem);
|
|
4994
5274
|
const route = [...routeSegments, ...pageSegment ? [pageSegment] : []].join("/");
|
|
4995
5275
|
const source = lstatSync4(path).size <= MAX_CONFIG_BYTES2 ? readFileSync4(path, "utf8") : "";
|
|
@@ -5014,7 +5294,7 @@ function registerPage(object, parentSegments, context) {
|
|
|
5014
5294
|
return registerPageAt(object.path, base, context, label, object.hidden === true) || null;
|
|
5015
5295
|
}
|
|
5016
5296
|
function convertNode(node, parentSegments, context) {
|
|
5017
|
-
const object =
|
|
5297
|
+
const object = objectValue4(node);
|
|
5018
5298
|
if (!object) return null;
|
|
5019
5299
|
if (typeof object.page === "string") {
|
|
5020
5300
|
const page = registerPage(object, parentSegments, context);
|
|
@@ -5102,7 +5382,7 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
5102
5382
|
}
|
|
5103
5383
|
if (!Array.isArray(config.versions)) return config;
|
|
5104
5384
|
const versions = config.versions.flatMap((entry) => {
|
|
5105
|
-
const version =
|
|
5385
|
+
const version = objectValue4(entry);
|
|
5106
5386
|
return version ? [version] : [];
|
|
5107
5387
|
});
|
|
5108
5388
|
const chosen = versions.find((version) => version.default === true) ?? versions[0];
|
|
@@ -5121,7 +5401,7 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
5121
5401
|
warnOnce2(context, "fern-version-not-file", `Fern version file "${chosen.path}" is not a regular file and was skipped.`);
|
|
5122
5402
|
return config;
|
|
5123
5403
|
}
|
|
5124
|
-
const versionConfig =
|
|
5404
|
+
const versionConfig = objectValue4(readBoundedYaml(versionPath));
|
|
5125
5405
|
if (!versionConfig) return config;
|
|
5126
5406
|
context.pathPrefix = relative4(fernRoot, dirname3(versionPath)).replace(/\\/g, "/");
|
|
5127
5407
|
return { ...config, navigation: versionConfig.navigation, tabs: versionConfig.tabs ?? config.tabs };
|
|
@@ -5136,13 +5416,13 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
5136
5416
|
}
|
|
5137
5417
|
function buildTabsFromConfig(rawConfig, configDir, fernRoot, routePrefix, fallbackTabLabel, context) {
|
|
5138
5418
|
const config = resolveVersionedNavigation(rawConfig, configDir, fernRoot, context);
|
|
5139
|
-
const tabsMeta =
|
|
5419
|
+
const tabsMeta = objectValue4(config.tabs) ?? {};
|
|
5140
5420
|
const navigation = config.navigation;
|
|
5141
|
-
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");
|
|
5142
5422
|
if (isTabbedNavigation) {
|
|
5143
5423
|
return navigation.flatMap((entry) => {
|
|
5144
5424
|
const id = String(entry.tab);
|
|
5145
|
-
const meta =
|
|
5425
|
+
const meta = objectValue4(tabsMeta[id]) ?? {};
|
|
5146
5426
|
const label = typeof meta["display-name"] === "string" ? meta["display-name"] : titleCase3(id);
|
|
5147
5427
|
const tabSegment = segmentFor(meta, label, context);
|
|
5148
5428
|
const segments = [...routePrefix, ...tabSegment ? [tabSegment] : []];
|
|
@@ -5164,7 +5444,7 @@ function buildTabsFromConfig(rawConfig, configDir, fernRoot, routePrefix, fallba
|
|
|
5164
5444
|
const index = visiblePages.find((page) => {
|
|
5165
5445
|
const descriptor = descriptors.get(page);
|
|
5166
5446
|
if (!descriptor) return false;
|
|
5167
|
-
const stem = basename2(descriptor.sourcePath,
|
|
5447
|
+
const stem = basename2(descriptor.sourcePath, extname5(descriptor.sourcePath));
|
|
5168
5448
|
return page === indexRoute || stem === "overview" || stem === "index";
|
|
5169
5449
|
});
|
|
5170
5450
|
const entries = visiblePages.filter((page) => page !== index).sort((left, right) => {
|
|
@@ -5213,7 +5493,7 @@ function buildTabsFromConfig(rawConfig, configDir, fernRoot, routePrefix, fallba
|
|
|
5213
5493
|
}
|
|
5214
5494
|
function projectFernProducts(rawProducts, fernRoot, context) {
|
|
5215
5495
|
return rawProducts.flatMap((entry) => {
|
|
5216
|
-
const product =
|
|
5496
|
+
const product = objectValue4(entry);
|
|
5217
5497
|
const rawPath = typeof product?.path === "string" ? product.path.trim() : "";
|
|
5218
5498
|
if (!product || !rawPath) return [];
|
|
5219
5499
|
const label = typeof product["display-name"] === "string" && product["display-name"].trim() ? product["display-name"].trim() : "Product";
|
|
@@ -5230,7 +5510,7 @@ function projectFernProducts(rawProducts, fernRoot, context) {
|
|
|
5230
5510
|
}
|
|
5231
5511
|
let productConfig;
|
|
5232
5512
|
try {
|
|
5233
|
-
productConfig =
|
|
5513
|
+
productConfig = objectValue4(readBoundedYaml(productPath));
|
|
5234
5514
|
} catch (error) {
|
|
5235
5515
|
warnOnce2(
|
|
5236
5516
|
context,
|
|
@@ -5284,7 +5564,7 @@ function projectFernNavigation(input) {
|
|
|
5284
5564
|
});
|
|
5285
5565
|
}
|
|
5286
5566
|
const navbarLinks = Array.isArray(config["navbar-links"]) ? config["navbar-links"].flatMap((entry) => {
|
|
5287
|
-
const link =
|
|
5567
|
+
const link = objectValue4(entry);
|
|
5288
5568
|
const href = typeof link?.url === "string" ? link.url : typeof link?.href === "string" ? link.href : typeof link?.value === "string" ? link.value : void 0;
|
|
5289
5569
|
const label = typeof link?.text === "string" ? link.text : typeof link?.label === "string" ? link.label : link?.type === "github" ? "GitHub" : void 0;
|
|
5290
5570
|
return href && label ? [{ label, href, ...link?.type === "github" ? { type: "github" } : {} }] : [];
|
|
@@ -5294,11 +5574,11 @@ function projectFernNavigation(input) {
|
|
|
5294
5574
|
navbarLinks.push(link);
|
|
5295
5575
|
}
|
|
5296
5576
|
}
|
|
5297
|
-
const announcement =
|
|
5577
|
+
const announcement = objectValue4(config.announcement);
|
|
5298
5578
|
const announcementMessage = typeof announcement?.message === "string" ? announcement.message.trim() : "";
|
|
5299
5579
|
const bannerContent = announcementMarkdown(announcementMessage);
|
|
5300
5580
|
const redirects = Array.isArray(config.redirects) ? config.redirects.flatMap((value) => {
|
|
5301
|
-
const redirect =
|
|
5581
|
+
const redirect = objectValue4(value);
|
|
5302
5582
|
if (!redirect || typeof redirect.source !== "string" || typeof redirect.destination !== "string") return [];
|
|
5303
5583
|
const rawSource = redirect.source.trim();
|
|
5304
5584
|
const rawDestination = redirect.destination.trim();
|
|
@@ -5384,9 +5664,16 @@ function projectFernNavigation(input) {
|
|
|
5384
5664
|
// src/repository.ts
|
|
5385
5665
|
var MAX_SOURCE_FILES = 5e3;
|
|
5386
5666
|
var MAX_PAGE_BYTES = 2e6;
|
|
5667
|
+
var FRONTMATTER_HEAD_BYTES = 65536;
|
|
5668
|
+
var MAX_WITHHELD_SCAN_BYTES = MAX_PAGE_BYTES * 8;
|
|
5669
|
+
var MAX_DROPPED_GATED_WARNINGS = 20;
|
|
5387
5670
|
var MAX_ASSET_BYTES = 25e6;
|
|
5388
5671
|
var MAX_TOTAL_ASSET_BYTES = 5e8;
|
|
5389
5672
|
var GIT_LFS_POINTER_PREFIX = "version https://git-lfs.github.com/spec/v1";
|
|
5673
|
+
function listAssetPaths(paths) {
|
|
5674
|
+
const shown = paths.slice(0, 5).join(", ");
|
|
5675
|
+
return paths.length > 5 ? `${shown}, and ${paths.length - 5} more` : shown;
|
|
5676
|
+
}
|
|
5390
5677
|
function isGitLfsPointer(content) {
|
|
5391
5678
|
return content.length < 1024 && content.toString("utf8", 0, GIT_LFS_POINTER_PREFIX.length) === GIT_LFS_POINTER_PREFIX;
|
|
5392
5679
|
}
|
|
@@ -5405,6 +5692,7 @@ var IGNORED_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
|
5405
5692
|
function isIgnoredContentDirectory(name) {
|
|
5406
5693
|
return name.startsWith(".") || name === "node_modules";
|
|
5407
5694
|
}
|
|
5695
|
+
var QUARANTINE_DIRECTORY = "migration-quarantine";
|
|
5408
5696
|
var ASSET_DIRECTORIES = /* @__PURE__ */ new Set(["assets", "images", "img", "media", "public", "static"]);
|
|
5409
5697
|
var ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
5410
5698
|
".avif",
|
|
@@ -5810,17 +6098,23 @@ function parseGitHubRepositoryUrl(rawUrl) {
|
|
|
5810
6098
|
}
|
|
5811
6099
|
var CLONE_RETRY_ATTEMPTS = 3;
|
|
5812
6100
|
var CLONE_RETRY_DELAY_MS = 1e3;
|
|
5813
|
-
var
|
|
6101
|
+
var DEFAULT_CLONE_IDLE_TIMEOUT_MS = 2 * 6e4;
|
|
6102
|
+
var MAX_GIT_PROCESS_MS = 60 * 6e4;
|
|
6103
|
+
var HARD_TIMEOUT_REASON = `still running after ${MAX_GIT_PROCESS_MS / 6e4} minutes`;
|
|
6104
|
+
var GIT_EXIT_DRAIN_MS = 2e3;
|
|
5814
6105
|
var RETRYABLE_CLONE_ERROR = /RPC failed|Recv failure|early EOF|curl \d+|Could not resolve host|Connection (?:reset|refused|timed out)|The remote end hung up|SSL[_ ]?(?:read|connect|write) error|timed out|network is unreachable/i;
|
|
5815
6106
|
function delay(ms) {
|
|
5816
6107
|
return new Promise((resolve3) => {
|
|
5817
6108
|
setTimeout(resolve3, ms);
|
|
5818
6109
|
});
|
|
5819
6110
|
}
|
|
5820
|
-
function
|
|
6111
|
+
function gitIdleTimeoutMs() {
|
|
5821
6112
|
const raw = process.env.THALLY_MIGRATE_CLONE_TIMEOUT_MS;
|
|
5822
6113
|
const parsed = raw ? Number(raw) : NaN;
|
|
5823
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed :
|
|
6114
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_CLONE_IDLE_TIMEOUT_MS;
|
|
6115
|
+
}
|
|
6116
|
+
function gitErrorTail(stderr) {
|
|
6117
|
+
return stderr.split(/[\r\n]+/).map((line) => line.trim()).filter((line) => line && !/^(?:remote: )?[A-Za-z][A-Za-z ]*:\s+\d+%/.test(line)).slice(-5).join("\n");
|
|
5824
6118
|
}
|
|
5825
6119
|
function runGit(args, options) {
|
|
5826
6120
|
return new Promise((resolve3, reject) => {
|
|
@@ -5830,24 +6124,66 @@ function runGit(args, options) {
|
|
|
5830
6124
|
env: { ...process.env, ...options.env }
|
|
5831
6125
|
});
|
|
5832
6126
|
let stderr = "";
|
|
5833
|
-
let
|
|
5834
|
-
|
|
5835
|
-
|
|
6127
|
+
let timeoutReason = "";
|
|
6128
|
+
let settled = false;
|
|
6129
|
+
let exitCode;
|
|
6130
|
+
let drainTimer;
|
|
6131
|
+
const idleMs = gitIdleTimeoutMs();
|
|
6132
|
+
const idleMessage = `no progress for ${idleMs < 1e3 ? `${idleMs}ms` : `${Math.round(idleMs / 1e3)}s`}`;
|
|
6133
|
+
const clearTimers = () => {
|
|
6134
|
+
clearTimeout(idleTimer);
|
|
6135
|
+
clearTimeout(hardTimer);
|
|
6136
|
+
clearTimeout(drainTimer);
|
|
6137
|
+
};
|
|
6138
|
+
const finish = (code) => {
|
|
6139
|
+
if (settled) return;
|
|
6140
|
+
settled = true;
|
|
6141
|
+
clearTimers();
|
|
6142
|
+
child.stderr.destroy();
|
|
6143
|
+
if (code === 0) resolve3();
|
|
6144
|
+
else if (timeoutReason) {
|
|
6145
|
+
const hint = timeoutReason === HARD_TIMEOUT_REASON ? "" : "; on a very slow link, raise THALLY_MIGRATE_CLONE_TIMEOUT_MS (milliseconds of allowed silence)";
|
|
6146
|
+
reject(new Error(`${options.label}: timed out (${timeoutReason}) and was killed. Check your network connection and try again${hint}.`));
|
|
6147
|
+
} else reject(new Error(`${options.label}: ${gitErrorTail(stderr) || `git exited ${code}`}`));
|
|
6148
|
+
};
|
|
6149
|
+
const kill = (reason) => {
|
|
6150
|
+
if (exitCode !== void 0) {
|
|
6151
|
+
finish(exitCode);
|
|
6152
|
+
return;
|
|
6153
|
+
}
|
|
6154
|
+
timeoutReason = reason;
|
|
5836
6155
|
child.kill("SIGKILL");
|
|
5837
|
-
}
|
|
6156
|
+
};
|
|
6157
|
+
let idleTimer = setTimeout(() => {
|
|
6158
|
+
kill(idleMessage);
|
|
6159
|
+
}, idleMs);
|
|
6160
|
+
const hardTimer = setTimeout(() => {
|
|
6161
|
+
kill(HARD_TIMEOUT_REASON);
|
|
6162
|
+
}, MAX_GIT_PROCESS_MS);
|
|
5838
6163
|
child.stderr.setEncoding("utf8");
|
|
5839
6164
|
child.stderr.on("data", (chunk) => {
|
|
5840
|
-
|
|
6165
|
+
stderr = (stderr + chunk).slice(-16e3);
|
|
6166
|
+
if (exitCode !== void 0 || settled) return;
|
|
6167
|
+
clearTimeout(idleTimer);
|
|
6168
|
+
idleTimer = setTimeout(() => {
|
|
6169
|
+
kill(idleMessage);
|
|
6170
|
+
}, idleMs);
|
|
5841
6171
|
});
|
|
5842
6172
|
child.on("error", (error) => {
|
|
5843
|
-
|
|
6173
|
+
if (settled) return;
|
|
6174
|
+
settled = true;
|
|
6175
|
+
clearTimers();
|
|
5844
6176
|
reject(error);
|
|
5845
6177
|
});
|
|
5846
|
-
child.on("close",
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
6178
|
+
child.on("close", finish);
|
|
6179
|
+
child.on("exit", (code) => {
|
|
6180
|
+
exitCode = code;
|
|
6181
|
+
clearTimeout(idleTimer);
|
|
6182
|
+
clearTimeout(hardTimer);
|
|
6183
|
+
if (timeoutReason) finish(code);
|
|
6184
|
+
else drainTimer = setTimeout(() => {
|
|
6185
|
+
finish(code);
|
|
6186
|
+
}, GIT_EXIT_DRAIN_MS);
|
|
5851
6187
|
});
|
|
5852
6188
|
});
|
|
5853
6189
|
}
|
|
@@ -5863,7 +6199,7 @@ var LFS_FILTER_OVERRIDE_ENV = {
|
|
|
5863
6199
|
GIT_CONFIG_VALUE_3: "false"
|
|
5864
6200
|
};
|
|
5865
6201
|
function cloneOnce(source, targetDir) {
|
|
5866
|
-
const args = ["clone", "--depth", "1", "--single-branch"];
|
|
6202
|
+
const args = ["clone", "--progress", "--depth", "1", "--single-branch"];
|
|
5867
6203
|
if (source.branch !== "HEAD") args.push("--branch", source.branch);
|
|
5868
6204
|
args.push("--", source.cloneUrl, targetDir);
|
|
5869
6205
|
return runGit(args, {
|
|
@@ -5912,6 +6248,7 @@ async function initSubmodules(targetDir, warnings) {
|
|
|
5912
6248
|
"submodule",
|
|
5913
6249
|
"update",
|
|
5914
6250
|
"--init",
|
|
6251
|
+
"--progress",
|
|
5915
6252
|
"--depth",
|
|
5916
6253
|
"1",
|
|
5917
6254
|
"--",
|
|
@@ -5971,7 +6308,7 @@ function containsMarkdown(directory, depth = 0) {
|
|
|
5971
6308
|
for (const entry of readdirSync2(directory, { withFileTypes: true })) {
|
|
5972
6309
|
if (isIgnoredDirectory(entry.name) || entry.isSymbolicLink()) continue;
|
|
5973
6310
|
const path = resolveWithin(directory, entry.name);
|
|
5974
|
-
if (entry.isFile() && [".md", ".mdx"].includes(
|
|
6311
|
+
if (entry.isFile() && [".md", ".mdx"].includes(extname6(entry.name).toLowerCase())) return true;
|
|
5975
6312
|
if (entry.isDirectory() && containsMarkdown(path, depth + 1)) return true;
|
|
5976
6313
|
}
|
|
5977
6314
|
return false;
|
|
@@ -5983,10 +6320,10 @@ function detectRepositoryDocsDir(repositoryDir) {
|
|
|
5983
6320
|
}
|
|
5984
6321
|
return "";
|
|
5985
6322
|
}
|
|
5986
|
-
var
|
|
5987
|
-
function scanFiles(root, confinementRoot = root, warnings, rank) {
|
|
6323
|
+
var RANKED_WALK_FACTOR = 20;
|
|
6324
|
+
function scanFiles(root, confinementRoot = root, warnings, rank, budget = MAX_SOURCE_FILES) {
|
|
5988
6325
|
const files = [];
|
|
5989
|
-
const walkCap = rank ?
|
|
6326
|
+
const walkCap = rank ? budget * RANKED_WALK_FACTOR : budget;
|
|
5990
6327
|
let confinementReal;
|
|
5991
6328
|
try {
|
|
5992
6329
|
confinementReal = realpathSync2(confinementRoot);
|
|
@@ -6056,10 +6393,10 @@ function scanFiles(root, confinementRoot = root, warnings, rank) {
|
|
|
6056
6393
|
}
|
|
6057
6394
|
return files;
|
|
6058
6395
|
}
|
|
6059
|
-
function selectGroupWithinBudget(scanned, rank, warnings, allVersionPrefixes, label) {
|
|
6060
|
-
if (scanned.length <=
|
|
6396
|
+
function selectGroupWithinBudget(scanned, rank, warnings, allVersionPrefixes, label, budget = MAX_SOURCE_FILES) {
|
|
6397
|
+
if (scanned.length <= budget) return scanned;
|
|
6061
6398
|
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));
|
|
6062
|
-
const dropped = ranked.slice(
|
|
6399
|
+
const dropped = ranked.slice(budget).map(({ file }) => file);
|
|
6063
6400
|
if (warnings) {
|
|
6064
6401
|
const droppedPages = label === "file" ? dropped.filter((file) => isDocumentationExtension(file.relativePath)) : dropped;
|
|
6065
6402
|
const noun = label === "file" ? "page" : "image or media file";
|
|
@@ -6072,18 +6409,18 @@ function selectGroupWithinBudget(scanned, rank, warnings, allVersionPrefixes, la
|
|
|
6072
6409
|
const rest = droppedPages.length - examples.length;
|
|
6073
6410
|
warnings.push({
|
|
6074
6411
|
code: "limit-reached",
|
|
6075
|
-
message: `This repository has more than ${
|
|
6412
|
+
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.")
|
|
6076
6413
|
});
|
|
6077
6414
|
}
|
|
6078
|
-
return ranked.slice(0,
|
|
6415
|
+
return ranked.slice(0, budget).map(({ file }) => file);
|
|
6079
6416
|
}
|
|
6080
|
-
function selectFilesWithinBudget(scanned, rank, warnings, allVersionPrefixes) {
|
|
6081
|
-
const isAsset = (file) => ASSET_EXTENSIONS.has(
|
|
6417
|
+
function selectFilesWithinBudget(scanned, rank, warnings, allVersionPrefixes, budget = MAX_SOURCE_FILES) {
|
|
6418
|
+
const isAsset = (file) => ASSET_EXTENSIONS.has(extname6(file.relativePath).toLowerCase());
|
|
6082
6419
|
const assetFiles = scanned.filter(isAsset);
|
|
6083
6420
|
const otherFiles = scanned.filter((file) => !isAsset(file));
|
|
6084
6421
|
return [
|
|
6085
|
-
...selectGroupWithinBudget(otherFiles, rank, warnings, allVersionPrefixes, "file"),
|
|
6086
|
-
...selectGroupWithinBudget(assetFiles, rank, warnings, allVersionPrefixes, "asset")
|
|
6422
|
+
...selectGroupWithinBudget(otherFiles, rank, warnings, allVersionPrefixes, "file", budget),
|
|
6423
|
+
...selectGroupWithinBudget(assetFiles, rank, warnings, allVersionPrefixes, "asset", budget)
|
|
6087
6424
|
];
|
|
6088
6425
|
}
|
|
6089
6426
|
function readMintignoreMatcher(mintlifyRoot) {
|
|
@@ -6128,6 +6465,45 @@ function normalizedReferenceKey(value) {
|
|
|
6128
6465
|
function exactReferenceKey(value) {
|
|
6129
6466
|
return value.split(/[?#]/, 1)[0].replace(/^\/+/, "").replace(/\\/g, "/").replace(/\.(?:mdx?|rst|txt)$/i, "");
|
|
6130
6467
|
}
|
|
6468
|
+
function specRefMatches(ref, specPath, specFilename, isDefault) {
|
|
6469
|
+
const prefix = /^(?:(\S+)\s+)?(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|TRACE|WEBHOOK)\s/i.exec(`${ref} `)?.[1];
|
|
6470
|
+
if (!prefix) return isDefault;
|
|
6471
|
+
const wanted = prefix.replace(/^\/+/, "").toLowerCase();
|
|
6472
|
+
const lowerPath = specPath.toLowerCase();
|
|
6473
|
+
return lowerPath === wanted || lowerPath.endsWith(`/${wanted}`) || specFilename.toLowerCase() === wanted;
|
|
6474
|
+
}
|
|
6475
|
+
var TOKEN_SEPARATORS = /[\s"'()<>[\]{}=,;:|\\/`*!?#&]+/;
|
|
6476
|
+
var PATH_SEPARATORS = /[\s"'()<>[\]{}=,;:|\\`*!?#&]+/;
|
|
6477
|
+
function publicAssetKey(repoRelative) {
|
|
6478
|
+
return repoRelative.split("/", 1)[0].toLowerCase() === "public" ? repoRelative.slice("public/".length) : repoRelative;
|
|
6479
|
+
}
|
|
6480
|
+
function addPathReferences(text, baseDir, exact, loose) {
|
|
6481
|
+
for (const word of text.split(PATH_SEPARATORS)) {
|
|
6482
|
+
let token = word.replace(/\.+$/, "");
|
|
6483
|
+
if (!/\.[a-z0-9]{2,5}$/i.test(token) || token.startsWith("//")) continue;
|
|
6484
|
+
if (token.includes("%")) {
|
|
6485
|
+
try {
|
|
6486
|
+
token = decodeURIComponent(token);
|
|
6487
|
+
} catch {
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
if (!token.includes("/")) {
|
|
6491
|
+
loose.add(token.toLowerCase());
|
|
6492
|
+
continue;
|
|
6493
|
+
}
|
|
6494
|
+
if (!token.startsWith("/") && baseDir === void 0) continue;
|
|
6495
|
+
const resolved = normalizeAssetPath(posix3.normalize(token.startsWith("/") ? token.slice(1) : posix3.join(baseDir ?? "", token)));
|
|
6496
|
+
if (!resolved) continue;
|
|
6497
|
+
const key = publicAssetKey(resolved);
|
|
6498
|
+
exact.add(key);
|
|
6499
|
+
loose.add(key.toLowerCase());
|
|
6500
|
+
}
|
|
6501
|
+
}
|
|
6502
|
+
function looselyNamed(assetPath, loose) {
|
|
6503
|
+
const lower = assetPath.toLowerCase();
|
|
6504
|
+
const name = lower.split("/").pop() ?? lower;
|
|
6505
|
+
return loose.has(lower) || loose.has(name) || loose.has(name.split(TOKEN_SEPARATORS).filter(Boolean).pop() ?? name);
|
|
6506
|
+
}
|
|
6131
6507
|
function findOpenApi(files) {
|
|
6132
6508
|
return files.find((file) => OPENAPI_FILENAMES.has(basename3(file.relativePath).toLowerCase()) && classifyApiSpec(file.absolutePath) === "openapi") ?? null;
|
|
6133
6509
|
}
|
|
@@ -6149,6 +6525,13 @@ function classifyApiSpec(path) {
|
|
|
6149
6525
|
if (!topLevel) return "unknown";
|
|
6150
6526
|
return /asyncapi/i.test(topLevel) ? "asyncapi" : "openapi";
|
|
6151
6527
|
}
|
|
6528
|
+
var SPEC_DIRECTORY = "openapi";
|
|
6529
|
+
function specAssetPath(filename) {
|
|
6530
|
+
return `${SPEC_DIRECTORY}/${filename}`;
|
|
6531
|
+
}
|
|
6532
|
+
function specAsset(filename, content) {
|
|
6533
|
+
return { path: specAssetPath(filename), content, projectRelative: true };
|
|
6534
|
+
}
|
|
6152
6535
|
function mintlifyTopLevelApiReferences(config) {
|
|
6153
6536
|
const api = config?.api && typeof config.api === "object" && !Array.isArray(config.api) ? config.api : null;
|
|
6154
6537
|
if (!api) return [];
|
|
@@ -6169,7 +6552,7 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs) {
|
|
|
6169
6552
|
const seen = /* @__PURE__ */ new Set();
|
|
6170
6553
|
const specs = [];
|
|
6171
6554
|
for (const reference of references) {
|
|
6172
|
-
const dedupeKey = `${reference.kind}:${reference.value}`;
|
|
6555
|
+
const dedupeKey = `${reference.kind}:${reference.value}:${reference.tabLabel ?? ""}`;
|
|
6173
6556
|
if (seen.has(dedupeKey)) continue;
|
|
6174
6557
|
seen.add(dedupeKey);
|
|
6175
6558
|
const tabSuffix = reference.tabLabel ? ` (tab "${reference.tabLabel}")` : "";
|
|
@@ -6181,7 +6564,7 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs) {
|
|
|
6181
6564
|
continue;
|
|
6182
6565
|
}
|
|
6183
6566
|
if (/^https:\/\//i.test(reference.value)) {
|
|
6184
|
-
remoteSpecs.push({ url: reference.value, ...reference.tabLabel ? { tabLabel: reference.tabLabel } : {} });
|
|
6567
|
+
remoteSpecs.push({ url: reference.value, ...reference.tabLabel ? { tabLabel: reference.tabLabel } : {}, ...reference.parentTab ? { parentTab: reference.parentTab } : {}, ...reference.icon ? { icon: reference.icon } : {}, ...reference.hidden ? { hidden: true } : {} });
|
|
6185
6568
|
warnings.push({
|
|
6186
6569
|
code: "unsupported-config",
|
|
6187
6570
|
message: `The remote OpenAPI spec "${reference.value}"${tabSuffix} requires a network download before this import is complete.`,
|
|
@@ -6212,15 +6595,19 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs) {
|
|
|
6212
6595
|
filename: basename3(match.relativePath),
|
|
6213
6596
|
content: readFileSync5(match.absolutePath),
|
|
6214
6597
|
tabLabel: reference.tabLabel,
|
|
6598
|
+
parentTab: reference.parentTab,
|
|
6599
|
+
icon: reference.icon,
|
|
6600
|
+
hidden: reference.hidden,
|
|
6215
6601
|
sourcePath: match.relativePath,
|
|
6216
6602
|
directory: reference.directory
|
|
6217
6603
|
});
|
|
6218
6604
|
}
|
|
6219
6605
|
const filenameOwners = /* @__PURE__ */ new Map();
|
|
6606
|
+
const filenameSources = /* @__PURE__ */ new Map();
|
|
6220
6607
|
for (const spec of specs) {
|
|
6221
6608
|
const groupKey = spec.tabLabel ?? "\0default";
|
|
6222
6609
|
const owner = filenameOwners.get(spec.filename);
|
|
6223
|
-
if (owner !== void 0 && owner !== groupKey) {
|
|
6610
|
+
if (owner !== void 0 && owner !== groupKey && filenameSources.get(spec.filename) !== spec.sourcePath) {
|
|
6224
6611
|
let disambiguated = spec.sourcePath.replace(/\//g, "-");
|
|
6225
6612
|
while (filenameOwners.has(disambiguated) && filenameOwners.get(disambiguated) !== groupKey) {
|
|
6226
6613
|
disambiguated = `${groupKey.replace(/\W+/g, "-")}-${disambiguated}`;
|
|
@@ -6228,6 +6615,7 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs) {
|
|
|
6228
6615
|
spec.filename = disambiguated;
|
|
6229
6616
|
}
|
|
6230
6617
|
filenameOwners.set(spec.filename, groupKey);
|
|
6618
|
+
filenameSources.set(spec.filename, spec.sourcePath);
|
|
6231
6619
|
}
|
|
6232
6620
|
return specs;
|
|
6233
6621
|
}
|
|
@@ -6282,7 +6670,7 @@ function apiOperationLinkMap(sources, docsConfig) {
|
|
|
6282
6670
|
const prefixLandings = /* @__PURE__ */ new Map();
|
|
6283
6671
|
const apiTabs = docsConfig.tabs.filter((tab) => !tab.hidden && tab.api);
|
|
6284
6672
|
apiTabs.forEach((tab, index) => {
|
|
6285
|
-
const source = sources.find((entry) => tab.api?.source ===
|
|
6673
|
+
const source = sources.find((entry) => tab.api?.source === specAssetPath(entry.filename));
|
|
6286
6674
|
if (!source || !source.prefix) return;
|
|
6287
6675
|
const specId = index === 0 ? "default" : slugifyApiSpecId(tab.tab);
|
|
6288
6676
|
const prefix = source.prefix.replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
@@ -6450,6 +6838,31 @@ function fernDefinitionExists(fernRoot, apiName) {
|
|
|
6450
6838
|
}
|
|
6451
6839
|
return candidateDirs.some((dir) => existsSync5(dir) && lstatSync5(dir).isDirectory());
|
|
6452
6840
|
}
|
|
6841
|
+
function readFrontmatterHead(path) {
|
|
6842
|
+
const fd = openSync(path, "r");
|
|
6843
|
+
try {
|
|
6844
|
+
const buffer = Buffer.alloc(FRONTMATTER_HEAD_BYTES);
|
|
6845
|
+
const head = buffer.toString("utf8", 0, readSync(fd, buffer, 0, buffer.length, 0));
|
|
6846
|
+
const opening = /^\uFEFF?---([^\r\n]*)\r?\n/.exec(head);
|
|
6847
|
+
if (!opening || opening[1].startsWith("-")) return head;
|
|
6848
|
+
return /^---[ \t]*\r?\n/m.test(head.slice(opening[0].length)) ? head : void 0;
|
|
6849
|
+
} finally {
|
|
6850
|
+
closeSync(fd);
|
|
6851
|
+
}
|
|
6852
|
+
}
|
|
6853
|
+
function canonicalPath(path) {
|
|
6854
|
+
try {
|
|
6855
|
+
return realpathSync2.native(path);
|
|
6856
|
+
} catch {
|
|
6857
|
+
return path;
|
|
6858
|
+
}
|
|
6859
|
+
}
|
|
6860
|
+
function pageHeadGateReason(raw) {
|
|
6861
|
+
const parsed = parseFrontmatter(raw);
|
|
6862
|
+
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;
|
|
6863
|
+
const openapi = typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0;
|
|
6864
|
+
return { reason: frontmatterGateReason(parsed.data) ?? unreadable, publicTrue: isPublicTrue(parsed.data.public), openapi };
|
|
6865
|
+
}
|
|
6453
6866
|
function withoutFrontmatter(value) {
|
|
6454
6867
|
return value.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
6455
6868
|
}
|
|
@@ -6584,7 +6997,7 @@ function fernSourceSiteUrl(config) {
|
|
|
6584
6997
|
function externalizeMissingFernLinks(body, pageIds, sourceSiteUrl, externalized) {
|
|
6585
6998
|
const rewrite = (target) => {
|
|
6586
6999
|
const path = target.split(/[?#]/, 1)[0].replace(/^\//, "").replace(/\/$/, "");
|
|
6587
|
-
if (!path || pageIds.has(path) || ASSET_EXTENSIONS.has(
|
|
7000
|
+
if (!path || pageIds.has(path) || ASSET_EXTENSIONS.has(extname6(path).toLowerCase())) return target;
|
|
6588
7001
|
try {
|
|
6589
7002
|
const base = new URL(`${sourceSiteUrl}/`);
|
|
6590
7003
|
const sourceLinkUrl = new URL(target.slice(1), base);
|
|
@@ -6682,7 +7095,15 @@ function preserveLinkedAnchors(pages) {
|
|
|
6682
7095
|
const key = comparable(cell.text);
|
|
6683
7096
|
candidateIndexes.set(key, [...candidateIndexes.get(key) ?? [], { index: cell.index, kind: "table" }]);
|
|
6684
7097
|
}
|
|
6685
|
-
|
|
7098
|
+
const headingSlugs = /* @__PURE__ */ new Map();
|
|
7099
|
+
const slugCounts = /* @__PURE__ */ new Map();
|
|
7100
|
+
for (const heading of headings) {
|
|
7101
|
+
const slug2 = heading.text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
7102
|
+
existing.add(slug2);
|
|
7103
|
+
const seen = slugCounts.get(slug2) ?? 0;
|
|
7104
|
+
slugCounts.set(slug2, seen + 1);
|
|
7105
|
+
if (slug2) headingSlugs.set(seen === 0 ? slug2 : `${slug2}-${seen}`, heading.index);
|
|
7106
|
+
}
|
|
6686
7107
|
const headingAliases = /* @__PURE__ */ new Map();
|
|
6687
7108
|
const tableAliases = /* @__PURE__ */ new Map();
|
|
6688
7109
|
for (const fragment of fragments) {
|
|
@@ -6696,6 +7117,14 @@ function preserveLinkedAnchors(pages) {
|
|
|
6696
7117
|
const selections = linkIndexes.map((index2) => matches.filter((candidate) => candidate.index < index2).at(-1));
|
|
6697
7118
|
if (selections.length && selections.every((candidate) => candidate?.index === selections[0]?.index)) match = selections[0];
|
|
6698
7119
|
}
|
|
7120
|
+
if (!match && matches.length > 1) {
|
|
7121
|
+
const headingMatches = matches.filter((candidate) => candidate.kind === "heading");
|
|
7122
|
+
if (headingMatches.length === 1) match = headingMatches[0];
|
|
7123
|
+
}
|
|
7124
|
+
if (!match) {
|
|
7125
|
+
const index2 = headingSlugs.get(fragment.toLowerCase());
|
|
7126
|
+
if (index2 !== void 0) match = { index: index2, kind: "heading" };
|
|
7127
|
+
}
|
|
6699
7128
|
if (!match) continue;
|
|
6700
7129
|
const { index, kind } = match;
|
|
6701
7130
|
const aliases = kind === "heading" ? headingAliases : tableAliases;
|
|
@@ -6762,8 +7191,13 @@ function globalSnippetAliases(files, repositoryRoot, siteRoot) {
|
|
|
6762
7191
|
if (conventionalAlias) aliases.set(conventionalAlias, file.absolutePath);
|
|
6763
7192
|
}
|
|
6764
7193
|
for (const file of files) {
|
|
6765
|
-
if (![".md", ".mdx"].includes(
|
|
6766
|
-
|
|
7194
|
+
if (![".md", ".mdx"].includes(extname6(file.relativePath).toLowerCase())) continue;
|
|
7195
|
+
let raw;
|
|
7196
|
+
try {
|
|
7197
|
+
raw = readFileSync5(file.absolutePath, "utf8");
|
|
7198
|
+
} catch {
|
|
7199
|
+
continue;
|
|
7200
|
+
}
|
|
6767
7201
|
for (const match of raw.matchAll(SNIPPET_IMPORT_PATTERN)) {
|
|
6768
7202
|
try {
|
|
6769
7203
|
const componentName = match[1] ?? match[2];
|
|
@@ -6790,7 +7224,7 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced) {
|
|
|
6790
7224
|
} catch {
|
|
6791
7225
|
return null;
|
|
6792
7226
|
}
|
|
6793
|
-
if (!ASSET_EXTENSIONS.has(
|
|
7227
|
+
if (!ASSET_EXTENSIONS.has(extname6(decodedPath).toLowerCase())) return null;
|
|
6794
7228
|
try {
|
|
6795
7229
|
const candidate = decodedPath.startsWith("/") ? resolveWithin(siteRoot, decodedPath.replace(/^\/+/, "")) : resolveWithinRoot(dirname4(currentFile), decodedPath, siteRoot);
|
|
6796
7230
|
const realRoot = realpathSync2(siteRoot);
|
|
@@ -6802,7 +7236,9 @@ function repositoryAssetHref(value, currentFile, siteRoot, onReferenced) {
|
|
|
6802
7236
|
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile()) return null;
|
|
6803
7237
|
const normalized = normalizeAssetPath(siteRelative);
|
|
6804
7238
|
if (!normalized) return null;
|
|
6805
|
-
|
|
7239
|
+
const onDiskRelative = relative5(realRoot, realpathSync2.native(candidate)).replace(/\\/g, "/");
|
|
7240
|
+
const onDisk = onDiskRelative.toLowerCase() === siteRelative.toLowerCase() ? normalizeAssetPath(onDiskRelative) : null;
|
|
7241
|
+
onReferenced?.(normalized, onDisk ?? void 0);
|
|
6806
7242
|
const rewritten = `/${normalized}${suffix}`;
|
|
6807
7243
|
return isBracketed ? `<${rewritten}>` : rewritten;
|
|
6808
7244
|
} catch {
|
|
@@ -6847,18 +7283,47 @@ function locallyDeclaredNames(raw) {
|
|
|
6847
7283
|
}
|
|
6848
7284
|
return names;
|
|
6849
7285
|
}
|
|
6850
|
-
function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0, siteRoot = repositoryRoot, globalAliases = /* @__PURE__ */ new Map()) {
|
|
7286
|
+
function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0, siteRoot = repositoryRoot, globalAliases = /* @__PURE__ */ new Map(), gate) {
|
|
6851
7287
|
if (depth >= 8) return raw;
|
|
6852
7288
|
raw = normalizeIndentedFences(raw);
|
|
6853
7289
|
const snippets = /* @__PURE__ */ new Map();
|
|
6854
7290
|
const preservedDeclarations = /* @__PURE__ */ new Map();
|
|
6855
|
-
|
|
7291
|
+
const source = relative5(repositoryRoot, currentFile).replace(/\\/g, "/");
|
|
7292
|
+
const blockInline = (candidate, shownPath, label = shownPath) => {
|
|
7293
|
+
const verdict = gate?.(candidate);
|
|
7294
|
+
if (!verdict) return void 0;
|
|
7295
|
+
const shown = shownPath.replace(/\*\//g, "* /");
|
|
7296
|
+
if (verdict.kind === "gated") {
|
|
7297
|
+
warnings.push({ code: "gated-page", message: `${label} is access-restricted and was NOT inlined; it was left as a comment.`, source });
|
|
7298
|
+
return `{/* Access-restricted content withheld: ${shown} */}`;
|
|
7299
|
+
}
|
|
7300
|
+
if (verdict.kind === "snippet-gated") {
|
|
7301
|
+
warnings.push({
|
|
7302
|
+
code: "gated-page",
|
|
7303
|
+
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.`,
|
|
7304
|
+
source
|
|
7305
|
+
});
|
|
7306
|
+
return `{/* Access-restricted content withheld: ${shown} */}`;
|
|
7307
|
+
}
|
|
7308
|
+
warnings.push({
|
|
7309
|
+
code: "skipped-file",
|
|
7310
|
+
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.`,
|
|
7311
|
+
source
|
|
7312
|
+
});
|
|
7313
|
+
return `{/* ${verdict.kind === "oversized" ? "Oversized" : "Unreadable"} content not inlined: ${shown} */}`;
|
|
7314
|
+
};
|
|
7315
|
+
let withoutImports = replaceOutsideCode(raw, (source2) => source2.replace(
|
|
6856
7316
|
SNIPPET_IMPORT_PATTERN,
|
|
6857
7317
|
(_statement, namedComponent, defaultComponent, sourcePath) => {
|
|
6858
7318
|
const componentName = namedComponent ?? defaultComponent;
|
|
6859
7319
|
try {
|
|
6860
7320
|
const candidate = resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot);
|
|
6861
7321
|
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile()) throw new Error("file not found");
|
|
7322
|
+
const blocked = blockInline(candidate, sourcePath, `Imported ${sourcePath}`);
|
|
7323
|
+
if (blocked) {
|
|
7324
|
+
snippets.set(componentName, blocked);
|
|
7325
|
+
return "";
|
|
7326
|
+
}
|
|
6862
7327
|
const nested = inlineMdxSnippets(
|
|
6863
7328
|
withoutFrontmatter(readFileSync5(candidate, "utf8")),
|
|
6864
7329
|
candidate,
|
|
@@ -6866,7 +7331,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
6866
7331
|
warnings,
|
|
6867
7332
|
depth + 1,
|
|
6868
7333
|
siteRoot,
|
|
6869
|
-
globalAliases
|
|
7334
|
+
globalAliases,
|
|
7335
|
+
gate
|
|
6870
7336
|
);
|
|
6871
7337
|
const declaration = statefulSnippetDeclaration(nested, componentName);
|
|
6872
7338
|
if (declaration) preservedDeclarations.set(componentName, declaration);
|
|
@@ -6883,7 +7349,7 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
6883
7349
|
}
|
|
6884
7350
|
}
|
|
6885
7351
|
));
|
|
6886
|
-
withoutImports = replaceOutsideCode(withoutImports, (
|
|
7352
|
+
withoutImports = replaceOutsideCode(withoutImports, (source2) => source2.replace(
|
|
6887
7353
|
SNIPPET_VALUE_IMPORT_PATTERN,
|
|
6888
7354
|
(statement, names, sourcePath) => {
|
|
6889
7355
|
const bindings = names.split(",").map((name) => name.trim()).filter(Boolean).map((name) => {
|
|
@@ -6893,7 +7359,16 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
6893
7359
|
if (bindings.length === 0 || bindings.some((binding) => !binding)) return statement;
|
|
6894
7360
|
try {
|
|
6895
7361
|
const candidate = resolveSnippetPath(sourcePath, currentFile, repositoryRoot, siteRoot);
|
|
6896
|
-
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile()) return statement;
|
|
7362
|
+
if (!existsSync5(candidate) || !lstatSync5(candidate).isFile() || gate?.(candidate)?.kind === "gated") return statement;
|
|
7363
|
+
const blocked = blockInline(candidate, sourcePath);
|
|
7364
|
+
if (blocked) {
|
|
7365
|
+
const declarations2 = [];
|
|
7366
|
+
for (const binding of bindings) {
|
|
7367
|
+
if (/^[A-Z]/.test(binding.exported)) snippets.set(binding.local, blocked);
|
|
7368
|
+
else declarations2.push(`export const ${binding.local} = undefined;`);
|
|
7369
|
+
}
|
|
7370
|
+
return declarations2.join("\n");
|
|
7371
|
+
}
|
|
6897
7372
|
const snippetSource = readFileSync5(candidate, "utf8");
|
|
6898
7373
|
const values = staticNamedSnippetValues(snippetSource);
|
|
6899
7374
|
const declarations = [];
|
|
@@ -6918,7 +7393,12 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
6918
7393
|
));
|
|
6919
7394
|
const localNames = locallyDeclaredNames(withoutImports);
|
|
6920
7395
|
for (const [componentName, candidate] of globalAliases) {
|
|
6921
|
-
if (snippets.has(componentName) || localNames.has(componentName) || !new RegExp(`<${componentName}(?:\\s|/?>)`).test(withoutImports)) continue;
|
|
7396
|
+
if (snippets.has(componentName) || localNames.has(componentName) || gate?.(candidate)?.kind === "gated" || !new RegExp(`<${componentName}(?:\\s|/?>)`).test(withoutImports)) continue;
|
|
7397
|
+
const blocked = blockInline(candidate, relative5(repositoryRoot, candidate).replace(/\\/g, "/"));
|
|
7398
|
+
if (blocked) {
|
|
7399
|
+
snippets.set(componentName, blocked);
|
|
7400
|
+
continue;
|
|
7401
|
+
}
|
|
6922
7402
|
const nested = inlineMdxSnippets(
|
|
6923
7403
|
withoutFrontmatter(readFileSync5(candidate, "utf8")),
|
|
6924
7404
|
candidate,
|
|
@@ -6926,7 +7406,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
6926
7406
|
warnings,
|
|
6927
7407
|
depth + 1,
|
|
6928
7408
|
siteRoot,
|
|
6929
|
-
globalAliases
|
|
7409
|
+
globalAliases,
|
|
7410
|
+
gate
|
|
6930
7411
|
);
|
|
6931
7412
|
const declaration = statefulSnippetDeclaration(nested, componentName);
|
|
6932
7413
|
if (declaration) preservedDeclarations.set(componentName, declaration);
|
|
@@ -6954,6 +7435,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
6954
7435
|
}
|
|
6955
7436
|
}).find((path) => path !== void 0 && existsSync5(path) && lstatSync5(path).isFile());
|
|
6956
7437
|
if (!candidate) throw new Error("file not found");
|
|
7438
|
+
const blocked = blockInline(candidate, filePath, `Snippet file="${filePath}"`);
|
|
7439
|
+
if (blocked) return blocked;
|
|
6957
7440
|
return inlineMdxSnippets(
|
|
6958
7441
|
withoutFrontmatter(readFileSync5(candidate, "utf8")),
|
|
6959
7442
|
candidate,
|
|
@@ -6961,7 +7444,8 @@ function inlineMdxSnippets(raw, currentFile, repositoryRoot, warnings, depth = 0
|
|
|
6961
7444
|
warnings,
|
|
6962
7445
|
depth + 1,
|
|
6963
7446
|
siteRoot,
|
|
6964
|
-
globalAliases
|
|
7447
|
+
globalAliases,
|
|
7448
|
+
gate
|
|
6965
7449
|
);
|
|
6966
7450
|
} catch {
|
|
6967
7451
|
warnings.push({
|
|
@@ -6982,25 +7466,30 @@ ${result.slice(prefix.length)}`;
|
|
|
6982
7466
|
return depth === 0 ? hoistMdxImports(result) : result;
|
|
6983
7467
|
}
|
|
6984
7468
|
function injectOpenApiSpecs(config, specs, warnings) {
|
|
6985
|
-
|
|
7469
|
+
const tabs = config.tabs.map((tab) => ({ ...tab }));
|
|
6986
7470
|
for (const spec of specs) {
|
|
6987
7471
|
const apiTab = spec.tabLabel ? tabs.find((tab) => tab.tab === spec.tabLabel) : tabs.find((tab) => tab.tab.toLowerCase().includes("api"));
|
|
6988
7472
|
if (apiTab) {
|
|
6989
7473
|
if (apiTab.api && warnings) {
|
|
6990
7474
|
warnings.push({
|
|
6991
7475
|
code: "unsupported-config",
|
|
6992
|
-
message: `Tab "${apiTab.tab}" already uses another OpenAPI spec, and Thally supports one API spec per tab, so "
|
|
7476
|
+
message: `Tab "${apiTab.tab}" already uses another OpenAPI spec, and Thally supports one API spec per tab, so "${specAssetPath(spec.filename)}" was not added. Put it in its own tab to include it.`
|
|
6993
7477
|
});
|
|
6994
7478
|
continue;
|
|
6995
7479
|
}
|
|
6996
7480
|
apiTab.api = {
|
|
6997
|
-
source:
|
|
7481
|
+
source: specAssetPath(spec.filename),
|
|
6998
7482
|
// An API-only tab needs generated endpoint navigation; an authored
|
|
6999
7483
|
// page tab keeps its own groups alongside the bound spec.
|
|
7000
7484
|
...apiTab.groups?.length || apiTab.pages?.length ? { navigation: false } : {}
|
|
7001
7485
|
};
|
|
7002
7486
|
} else {
|
|
7003
|
-
tabs
|
|
7487
|
+
insertApiTab(tabs, {
|
|
7488
|
+
tab: spec.tabLabel ?? "API Reference",
|
|
7489
|
+
...spec.icon ? { icon: spec.icon } : {},
|
|
7490
|
+
...spec.hidden ? { hidden: true } : {},
|
|
7491
|
+
api: { source: specAssetPath(spec.filename) }
|
|
7492
|
+
}, spec.parentTab);
|
|
7004
7493
|
}
|
|
7005
7494
|
}
|
|
7006
7495
|
return { ...config, tabs };
|
|
@@ -7048,6 +7537,7 @@ function migrateRepository(options) {
|
|
|
7048
7537
|
const referenceOrder = /* @__PURE__ */ new Map();
|
|
7049
7538
|
let docusaurusSidebars = null;
|
|
7050
7539
|
let mintlifyConfig = null;
|
|
7540
|
+
const mintlifyGatedRefs = /* @__PURE__ */ new Map();
|
|
7051
7541
|
let fernRawConfig = null;
|
|
7052
7542
|
let fernBasePath = "";
|
|
7053
7543
|
let fernVersionPath = "";
|
|
@@ -7068,6 +7558,10 @@ function migrateRepository(options) {
|
|
|
7068
7558
|
const projected = projectMintlifyNavigation(config);
|
|
7069
7559
|
docsConfig = projected.docsConfig;
|
|
7070
7560
|
warnings.push(...projected.warnings);
|
|
7561
|
+
for (const gated of projected.gatedReferences) {
|
|
7562
|
+
const gatedKey = normalizedReferenceKey(gated.ref).toLowerCase();
|
|
7563
|
+
if (!mintlifyGatedRefs.has(gatedKey)) mintlifyGatedRefs.set(gatedKey, gated.reason);
|
|
7564
|
+
}
|
|
7071
7565
|
for (const [index, reference] of projected.pageReferences.entries()) {
|
|
7072
7566
|
const key = normalizedReferenceKey(reference.ref);
|
|
7073
7567
|
if (!referenceMap.has(key)) referenceMap.set(key, reference);
|
|
@@ -7153,10 +7647,11 @@ function migrateRepository(options) {
|
|
|
7153
7647
|
const versionCeiling = lastReferencedIndexByVersionLocale.get(blockKey);
|
|
7154
7648
|
return versionCeiling !== void 0 ? versionCeiling + 1 : Number.MAX_SAFE_INTEGER;
|
|
7155
7649
|
} : void 0;
|
|
7156
|
-
const
|
|
7650
|
+
const sourceBudget = options.maxSourceFiles ?? MAX_SOURCE_FILES;
|
|
7651
|
+
const scannedFiles = scanFiles(contentRoot, repositoryDir, warnings, discoveryRank, sourceBudget);
|
|
7157
7652
|
const mintignoreFilteredFiles = mintignoreMatcher ? scannedFiles.filter((file) => !mintignoreMatcher.ignores(file.relativePath)) : scannedFiles;
|
|
7158
|
-
const discoveryBudgetApplied = discoveryRank !== void 0 && mintignoreFilteredFiles.length >
|
|
7159
|
-
const files = discoveryRank ? selectFilesWithinBudget(mintignoreFilteredFiles, discoveryRank, warnings, allVersionPrefixes) : mintignoreFilteredFiles;
|
|
7653
|
+
const discoveryBudgetApplied = discoveryRank !== void 0 && mintignoreFilteredFiles.length > sourceBudget;
|
|
7654
|
+
const files = discoveryRank ? selectFilesWithinBudget(mintignoreFilteredFiles, discoveryRank, warnings, allVersionPrefixes, sourceBudget) : mintignoreFilteredFiles;
|
|
7160
7655
|
if (platform === "fern" && fernProjectRoot) {
|
|
7161
7656
|
const discovered2 = new Set(files.map((file) => file.relativePath));
|
|
7162
7657
|
for (const sourcePath of fernExternalSourcePaths) {
|
|
@@ -7186,6 +7681,108 @@ function migrateRepository(options) {
|
|
|
7186
7681
|
const docusaurusDescriptors = [];
|
|
7187
7682
|
const docusaurusDocCardRoutes = /* @__PURE__ */ new Set();
|
|
7188
7683
|
const seenPageIds = /* @__PURE__ */ new Set();
|
|
7684
|
+
const quarantinedFiles = [];
|
|
7685
|
+
const withheldAssetPaths = /* @__PURE__ */ new Set();
|
|
7686
|
+
const publishedExact = /* @__PURE__ */ new Set();
|
|
7687
|
+
const publishedLoose = /* @__PURE__ */ new Set();
|
|
7688
|
+
const withholdAsset = (assetPath, onDiskSpelling) => {
|
|
7689
|
+
withheldAssetPaths.add(publicAssetKey(onDiskSpelling ?? assetPath));
|
|
7690
|
+
};
|
|
7691
|
+
let sawPublicTrue = false;
|
|
7692
|
+
const gateByPath = /* @__PURE__ */ new Map();
|
|
7693
|
+
const withheldSpecRefs = [];
|
|
7694
|
+
const publishedSpecRefs = [];
|
|
7695
|
+
const withheldPaths = /* @__PURE__ */ new Set();
|
|
7696
|
+
const isDocFile = (file) => [".md", ".mdx"].includes(extname6(file.relativePath).toLowerCase());
|
|
7697
|
+
const isGateCandidate = (file) => isDocFile(file) && !file.relativePath.split("/").some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase()));
|
|
7698
|
+
const classifyPageGate = (file) => {
|
|
7699
|
+
let head = { publicTrue: false };
|
|
7700
|
+
let unreadableGate;
|
|
7701
|
+
try {
|
|
7702
|
+
const size = lstatSync5(file.absolutePath).size;
|
|
7703
|
+
const raw = readFrontmatterHead(file.absolutePath) ?? (size > MAX_PAGE_BYTES ? void 0 : readFileSync5(file.absolutePath, "utf8"));
|
|
7704
|
+
if (raw === void 0) throw new Error("frontmatter is not terminated within the bounded read");
|
|
7705
|
+
head = pageHeadGateReason(raw);
|
|
7706
|
+
} catch {
|
|
7707
|
+
unreadableGate = "frontmatter could not be read";
|
|
7708
|
+
}
|
|
7709
|
+
const reason = head.reason ?? unreadableGate ?? mintlifyGatedRefs.get(normalizedReferenceKey(file.relativePath).toLowerCase());
|
|
7710
|
+
return { reason, publicTrue: head.publicTrue, openapi: head.openapi };
|
|
7711
|
+
};
|
|
7712
|
+
const keptFiles = new Set(files);
|
|
7713
|
+
const droppedGatedFiles = [];
|
|
7714
|
+
const withheldDocFiles = [];
|
|
7715
|
+
let droppedPageCount = 0;
|
|
7716
|
+
const scanTruncated = scannedFiles.length >= (discoveryRank ? sourceBudget * RANKED_WALK_FACTOR : sourceBudget);
|
|
7717
|
+
if (platform === "mintlify") {
|
|
7718
|
+
for (const file of files) {
|
|
7719
|
+
if (isDocFile(file) && !isGateCandidate(file)) {
|
|
7720
|
+
const snippetReason = classifyPageGate(file).reason;
|
|
7721
|
+
if (snippetReason) withheldDocFiles.push(file);
|
|
7722
|
+
continue;
|
|
7723
|
+
}
|
|
7724
|
+
if (!isGateCandidate(file)) continue;
|
|
7725
|
+
const verdict = classifyPageGate(file);
|
|
7726
|
+
gateByPath.set(file.absolutePath, { reason: verdict.reason, publicTrue: verdict.publicTrue });
|
|
7727
|
+
if (verdict.openapi) (verdict.reason ? withheldSpecRefs : publishedSpecRefs).push(verdict.openapi);
|
|
7728
|
+
if (verdict.reason) withheldPaths.add(file.absolutePath);
|
|
7729
|
+
}
|
|
7730
|
+
for (const file of mintignoreFilteredFiles) {
|
|
7731
|
+
if (keptFiles.has(file) || !isDocFile(file)) continue;
|
|
7732
|
+
droppedPageCount++;
|
|
7733
|
+
if (!isGateCandidate(file)) {
|
|
7734
|
+
const snippetReason = classifyPageGate(file).reason;
|
|
7735
|
+
if (snippetReason) {
|
|
7736
|
+
withheldDocFiles.push(file);
|
|
7737
|
+
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 });
|
|
7738
|
+
}
|
|
7739
|
+
continue;
|
|
7740
|
+
}
|
|
7741
|
+
const verdict = classifyPageGate(file);
|
|
7742
|
+
if (!verdict.reason) continue;
|
|
7743
|
+
if (verdict.openapi) withheldSpecRefs.push(verdict.openapi);
|
|
7744
|
+
withheldPaths.add(file.absolutePath);
|
|
7745
|
+
droppedGatedFiles.push({ file, reason: verdict.reason });
|
|
7746
|
+
}
|
|
7747
|
+
for (const file of scannedFiles) {
|
|
7748
|
+
if (!mintignoreMatcher?.ignores(file.relativePath) || !isDocFile(file)) continue;
|
|
7749
|
+
const verdict = classifyPageGate(file);
|
|
7750
|
+
if (!verdict.reason) continue;
|
|
7751
|
+
if (verdict.openapi) withheldSpecRefs.push(verdict.openapi);
|
|
7752
|
+
withheldDocFiles.push(file);
|
|
7753
|
+
}
|
|
7754
|
+
}
|
|
7755
|
+
const pagesNotClassified = droppedPageCount > 0 || platform === "mintlify" && scanTruncated;
|
|
7756
|
+
const hasWithheldContent = withheldPaths.size > 0 || withheldDocFiles.length > 0;
|
|
7757
|
+
const trackPublishedRefs = platform === "mintlify" && (hasWithheldContent || pagesNotClassified);
|
|
7758
|
+
const withheldCanonical = new Set([...withheldPaths].map(canonicalPath));
|
|
7759
|
+
const verdictCache = /* @__PURE__ */ new Map();
|
|
7760
|
+
const classifyInlineCandidate = (candidate, key) => {
|
|
7761
|
+
if (withheldCanonical.has(key)) return { kind: "gated", reason: "access-restricted" };
|
|
7762
|
+
const shown = relative5(contentRoot, candidate).replace(/\\/g, "/");
|
|
7763
|
+
const inSnippetDirectory = shown.split("/").some((segment) => SNIPPET_DIRECTORIES.has(segment.toLowerCase()));
|
|
7764
|
+
let head;
|
|
7765
|
+
try {
|
|
7766
|
+
head = readFrontmatterHead(candidate);
|
|
7767
|
+
} catch {
|
|
7768
|
+
return { kind: "unreadable", reason: "could not be read" };
|
|
7769
|
+
}
|
|
7770
|
+
const gated = head === void 0 ? void 0 : pageHeadGateReason(head).reason;
|
|
7771
|
+
if (gated) return { kind: inSnippetDirectory ? "snippet-gated" : "gated", reason: gated };
|
|
7772
|
+
const navigationGated = inSnippetDirectory ? void 0 : mintlifyGatedRefs.get(normalizedReferenceKey(shown).toLowerCase());
|
|
7773
|
+
if (navigationGated) return { kind: "gated", reason: navigationGated };
|
|
7774
|
+
if (lstatSync5(candidate).size > MAX_PAGE_BYTES) return { kind: "oversized", reason: "over 2 MB" };
|
|
7775
|
+
return head === void 0 ? { kind: "unreadable", reason: "frontmatter not terminated in the bounded read" } : void 0;
|
|
7776
|
+
};
|
|
7777
|
+
const inlineGate = platform === "mintlify" ? (candidate) => {
|
|
7778
|
+
const key = canonicalPath(candidate);
|
|
7779
|
+
let verdict = verdictCache.get(key);
|
|
7780
|
+
if (verdict === void 0) {
|
|
7781
|
+
verdict = classifyInlineCandidate(candidate, key) ?? null;
|
|
7782
|
+
verdictCache.set(key, verdict);
|
|
7783
|
+
}
|
|
7784
|
+
return verdict ?? void 0;
|
|
7785
|
+
} : void 0;
|
|
7189
7786
|
const fernIdRenames = /* @__PURE__ */ new Map();
|
|
7190
7787
|
let skipped = 0;
|
|
7191
7788
|
let discovered = files.length;
|
|
@@ -7198,6 +7795,47 @@ function migrateRepository(options) {
|
|
|
7198
7795
|
return leftOrder - rightOrder || left.relativePath.localeCompare(right.relativePath);
|
|
7199
7796
|
});
|
|
7200
7797
|
const snippetAliases = platform === "mintlify" && mintlifyProjectRoot ? globalSnippetAliases(files, repositoryDir, mintlifyProjectRoot) : /* @__PURE__ */ new Map();
|
|
7798
|
+
droppedGatedFiles.forEach(({ file, reason }, index) => {
|
|
7799
|
+
if (index < MAX_DROPPED_GATED_WARNINGS) {
|
|
7800
|
+
warnings.push({
|
|
7801
|
+
code: "gated-page",
|
|
7802
|
+
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.`,
|
|
7803
|
+
source: file.relativePath
|
|
7804
|
+
});
|
|
7805
|
+
}
|
|
7806
|
+
if (!mintlifyProjectRoot) return;
|
|
7807
|
+
try {
|
|
7808
|
+
if (lstatSync5(file.absolutePath).size > MAX_WITHHELD_SCAN_BYTES) throw new Error("too large to scan");
|
|
7809
|
+
rewriteRepositoryAssetLinks(
|
|
7810
|
+
inlineMdxSnippets(readFileSync5(file.absolutePath, "utf8"), file.absolutePath, repositoryDir, [], 0, mintlifyProjectRoot, snippetAliases, inlineGate),
|
|
7811
|
+
file.absolutePath,
|
|
7812
|
+
mintlifyProjectRoot,
|
|
7813
|
+
withholdAsset
|
|
7814
|
+
);
|
|
7815
|
+
} catch {
|
|
7816
|
+
}
|
|
7817
|
+
});
|
|
7818
|
+
for (const file of withheldDocFiles) {
|
|
7819
|
+
try {
|
|
7820
|
+
if (!mintlifyProjectRoot) continue;
|
|
7821
|
+
if (lstatSync5(file.absolutePath).size > MAX_WITHHELD_SCAN_BYTES) {
|
|
7822
|
+
warnings.push({
|
|
7823
|
+
code: "gated-page",
|
|
7824
|
+
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.",
|
|
7825
|
+
source: file.relativePath
|
|
7826
|
+
});
|
|
7827
|
+
continue;
|
|
7828
|
+
}
|
|
7829
|
+
rewriteRepositoryAssetLinks(readFileSync5(file.absolutePath, "utf8"), file.absolutePath, mintlifyProjectRoot, withholdAsset);
|
|
7830
|
+
} catch {
|
|
7831
|
+
}
|
|
7832
|
+
}
|
|
7833
|
+
if (droppedGatedFiles.length > MAX_DROPPED_GATED_WARNINGS) {
|
|
7834
|
+
warnings.push({
|
|
7835
|
+
code: "gated-page",
|
|
7836
|
+
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.`
|
|
7837
|
+
});
|
|
7838
|
+
}
|
|
7201
7839
|
for (const file of pageFiles) {
|
|
7202
7840
|
if (!isDocumentationExtension(file.relativePath)) continue;
|
|
7203
7841
|
if (platform === "fern" && !exactReferenceMap.has(exactReferenceKey(file.relativePath)) && !referenceMap.has(normalizedReferenceKey(file.relativePath))) {
|
|
@@ -7214,7 +7852,7 @@ function migrateRepository(options) {
|
|
|
7214
7852
|
skipped++;
|
|
7215
7853
|
continue;
|
|
7216
7854
|
}
|
|
7217
|
-
if (![".md", ".mdx"].includes(
|
|
7855
|
+
if (![".md", ".mdx"].includes(extname6(file.relativePath).toLowerCase())) {
|
|
7218
7856
|
skipped++;
|
|
7219
7857
|
warnings.push({ code: "skipped-file", message: "Only Markdown and MDX are imported without an explicit converter.", source: file.relativePath });
|
|
7220
7858
|
continue;
|
|
@@ -7223,9 +7861,64 @@ function migrateRepository(options) {
|
|
|
7223
7861
|
if (size > MAX_PAGE_BYTES) {
|
|
7224
7862
|
skipped++;
|
|
7225
7863
|
warnings.push({ code: "skipped-file", message: "Page exceeded the 2 MB repository import limit.", source: file.relativePath });
|
|
7864
|
+
const oversizedGate = gateByPath.get(file.absolutePath)?.reason;
|
|
7865
|
+
if (oversizedGate) {
|
|
7866
|
+
warnings.push({
|
|
7867
|
+
code: "gated-page",
|
|
7868
|
+
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.`,
|
|
7869
|
+
source: file.relativePath
|
|
7870
|
+
});
|
|
7871
|
+
}
|
|
7872
|
+
if (oversizedGate && mintlifyProjectRoot) {
|
|
7873
|
+
if (size > MAX_WITHHELD_SCAN_BYTES) {
|
|
7874
|
+
warnings.push({
|
|
7875
|
+
code: "gated-page",
|
|
7876
|
+
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.",
|
|
7877
|
+
source: file.relativePath
|
|
7878
|
+
});
|
|
7879
|
+
} else {
|
|
7880
|
+
rewriteRepositoryAssetLinks(readFileSync5(file.absolutePath, "utf8"), file.absolutePath, mintlifyProjectRoot, withholdAsset);
|
|
7881
|
+
}
|
|
7882
|
+
}
|
|
7226
7883
|
continue;
|
|
7227
7884
|
}
|
|
7228
7885
|
const key = normalizedReferenceKey(file.relativePath);
|
|
7886
|
+
if (platform === "mintlify") {
|
|
7887
|
+
const gate = gateByPath.get(file.absolutePath);
|
|
7888
|
+
if (gate?.publicTrue) sawPublicTrue = true;
|
|
7889
|
+
const gateReason = gate?.reason;
|
|
7890
|
+
if (gateReason) {
|
|
7891
|
+
skipped++;
|
|
7892
|
+
if (file.relativePath.split("/").some((segment) => segment === "..")) {
|
|
7893
|
+
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 });
|
|
7894
|
+
continue;
|
|
7895
|
+
}
|
|
7896
|
+
try {
|
|
7897
|
+
if (mintlifyProjectRoot) {
|
|
7898
|
+
rewriteRepositoryAssetLinks(
|
|
7899
|
+
inlineMdxSnippets(readFileSync5(file.absolutePath, "utf8"), file.absolutePath, repositoryDir, [], 0, mintlifyProjectRoot, snippetAliases, inlineGate),
|
|
7900
|
+
file.absolutePath,
|
|
7901
|
+
mintlifyProjectRoot,
|
|
7902
|
+
withholdAsset
|
|
7903
|
+
);
|
|
7904
|
+
}
|
|
7905
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/${file.relativePath}`, content: readFileSync5(file.absolutePath) });
|
|
7906
|
+
} catch {
|
|
7907
|
+
warnings.push({
|
|
7908
|
+
code: "gated-page",
|
|
7909
|
+
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.`,
|
|
7910
|
+
source: file.relativePath
|
|
7911
|
+
});
|
|
7912
|
+
continue;
|
|
7913
|
+
}
|
|
7914
|
+
warnings.push({
|
|
7915
|
+
code: "gated-page",
|
|
7916
|
+
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.`,
|
|
7917
|
+
source: file.relativePath
|
|
7918
|
+
});
|
|
7919
|
+
continue;
|
|
7920
|
+
}
|
|
7921
|
+
}
|
|
7229
7922
|
const referenced = exactReferenceMap.get(exactReferenceKey(file.relativePath)) ?? referenceMap.get(key);
|
|
7230
7923
|
let locale = referenced?.locale;
|
|
7231
7924
|
let navigationId = referenced?.navigationId ?? pageIdFromReference(file.relativePath, platform === "mintlify");
|
|
@@ -7252,8 +7945,11 @@ function migrateRepository(options) {
|
|
|
7252
7945
|
warnings,
|
|
7253
7946
|
0,
|
|
7254
7947
|
mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir,
|
|
7255
|
-
snippetAliases
|
|
7948
|
+
snippetAliases,
|
|
7949
|
+
inlineGate
|
|
7256
7950
|
);
|
|
7951
|
+
const publishedText = trackPublishedRefs ? raw : void 0;
|
|
7952
|
+
const pageAssetReferences = [];
|
|
7257
7953
|
if (platform === "fern" || platform === "mintlify" || platform === "docusaurus") {
|
|
7258
7954
|
raw = normalizeExplicitHeadingIds(raw);
|
|
7259
7955
|
}
|
|
@@ -7345,8 +8041,8 @@ function migrateRepository(options) {
|
|
|
7345
8041
|
}
|
|
7346
8042
|
if (platform === "fern" && fernHiddenIds.has(navigationId)) page.hidden = true;
|
|
7347
8043
|
if (platform === "mintlify" && mintlifyProjectRoot) {
|
|
7348
|
-
page.body = rewriteRepositoryAssetLinks(page.body, file.absolutePath, mintlifyProjectRoot, (assetPath) => {
|
|
7349
|
-
|
|
8044
|
+
page.body = rewriteRepositoryAssetLinks(page.body, file.absolutePath, mintlifyProjectRoot, (assetPath, onDiskSpelling) => {
|
|
8045
|
+
pageAssetReferences.push(publicAssetKey(onDiskSpelling ?? assetPath));
|
|
7350
8046
|
});
|
|
7351
8047
|
}
|
|
7352
8048
|
if (platform === "fern" && fernProjectRoot) {
|
|
@@ -7410,6 +8106,8 @@ function migrateRepository(options) {
|
|
|
7410
8106
|
}
|
|
7411
8107
|
seenPageIds.add(page.id);
|
|
7412
8108
|
pages.push(page);
|
|
8109
|
+
for (const assetPath of pageAssetReferences) addAssetReference(assetPath, file.relativePath);
|
|
8110
|
+
if (publishedText !== void 0) addPathReferences(publishedText, posix3.dirname(file.relativePath).replace(/^\.$/, ""), publishedExact, publishedLoose);
|
|
7413
8111
|
if (hasDocusaurusDocCardList) {
|
|
7414
8112
|
docusaurusDocCardRoutes.add(page.navigationId);
|
|
7415
8113
|
}
|
|
@@ -7452,7 +8150,7 @@ function migrateRepository(options) {
|
|
|
7452
8150
|
const withinFern = relative5(fernProjectRoot, absolutePath);
|
|
7453
8151
|
const assetRelative = withinFern !== ".." && !withinFern.startsWith(`..${sep2}`) && !isAbsolute2(withinFern) ? withinFern : relative5(repositoryDir, absolutePath);
|
|
7454
8152
|
const assetPath = normalizeAssetPath(assetRelative.replace(/\\/g, "/"));
|
|
7455
|
-
if (!assetPath || !ASSET_EXTENSIONS.has(
|
|
8153
|
+
if (!assetPath || !ASSET_EXTENSIONS.has(extname6(assetPath).toLowerCase())) return;
|
|
7456
8154
|
if (source) {
|
|
7457
8155
|
fernBrandAssetPaths.set(source, assetPath);
|
|
7458
8156
|
addAssetReference(assetPath, "fern/docs.yml");
|
|
@@ -7483,45 +8181,131 @@ function migrateRepository(options) {
|
|
|
7483
8181
|
}));
|
|
7484
8182
|
}) : [];
|
|
7485
8183
|
const assetCandidates = [];
|
|
8184
|
+
const mintlifyFonts = platform === "mintlify" ? mintlifyFontSources(mintlifyConfig) : [];
|
|
8185
|
+
const fontRelativePaths = new Set(mintlifyFonts.flatMap((font) => font.path ? [font.path] : []));
|
|
8186
|
+
const importedComponentSources = new Set((componentMigrator?.files() ?? []).flatMap((file) => {
|
|
8187
|
+
const source = /^src\/mdx\/migrated\/[^/]+\/source\/(.+)$/.exec(file.path)?.[1];
|
|
8188
|
+
return source ? [resolvePath(repositoryDir, source)] : [];
|
|
8189
|
+
}));
|
|
8190
|
+
const siteAssetPaths = /* @__PURE__ */ new Map();
|
|
8191
|
+
const fontAssetByRelative = /* @__PURE__ */ new Map();
|
|
7486
8192
|
for (const file of [...files, ...repositoryAssets, ...fernReferencedAssets]) {
|
|
7487
8193
|
const firstSegment = file.relativePath.split("/", 1)[0].toLowerCase();
|
|
7488
|
-
|
|
8194
|
+
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;
|
|
8195
|
+
if (!siteKind && !ASSET_EXTENSIONS.has(extname6(file.relativePath).toLowerCase())) continue;
|
|
7489
8196
|
if (platform !== "mintlify" && !ASSET_DIRECTORIES.has(firstSegment) && !(platform === "fern" && referencedAssetPaths.has(file.relativePath))) continue;
|
|
7490
8197
|
const isDocusaurusStatic = platform === "docusaurus" && firstSegment === "static";
|
|
7491
8198
|
const assetPath = normalizeAssetPath(firstSegment === "public" ? file.relativePath.slice("public/".length) : isDocusaurusStatic ? file.relativePath.slice("static/".length) : file.relativePath);
|
|
7492
8199
|
if (!assetPath) continue;
|
|
7493
|
-
|
|
8200
|
+
if (siteKind) {
|
|
8201
|
+
siteAssetPaths.set(assetPath, siteKind);
|
|
8202
|
+
if (siteKind === "font") fontAssetByRelative.set(file.relativePath, assetPath);
|
|
8203
|
+
}
|
|
8204
|
+
const candidateSize = lstatSync5(file.absolutePath).size;
|
|
8205
|
+
if (siteKind === "style" || siteKind === "script") {
|
|
8206
|
+
if (trackPublishedRefs && candidateSize <= MAX_PAGE_BYTES) addPathReferences(readFileSync5(file.absolutePath, "utf8"), posix3.dirname(file.relativePath).replace(/^\.$/, ""), publishedExact, publishedLoose);
|
|
8207
|
+
}
|
|
8208
|
+
assetCandidates.push({ file, assetPath, size: candidateSize });
|
|
7494
8209
|
}
|
|
7495
8210
|
const isReferenced = (candidate) => referencedAssetPaths.has(candidate.assetPath);
|
|
7496
8211
|
const orderedAssetCandidates = [
|
|
7497
8212
|
...assetCandidates.filter((candidate) => isReferenced(candidate)),
|
|
7498
8213
|
...assetCandidates.filter((candidate) => !isReferenced(candidate))
|
|
7499
8214
|
];
|
|
8215
|
+
const quarantinedPageCount = quarantinedFiles.length;
|
|
8216
|
+
const destinationCounts = /* @__PURE__ */ new Map();
|
|
8217
|
+
for (const { assetPath } of assetCandidates) destinationCounts.set(assetPath, (destinationCounts.get(assetPath) ?? 0) + 1);
|
|
8218
|
+
if (trackPublishedRefs) {
|
|
8219
|
+
const publishedConfig = JSON.stringify(mintlifyConfig ?? {}, (_key, value) => value && typeof value === "object" && !Array.isArray(value) && navigationGateReason(value) ? void 0 : value);
|
|
8220
|
+
addPathReferences(publishedConfig, "", publishedExact, publishedLoose);
|
|
8221
|
+
}
|
|
8222
|
+
for (const file of componentMigrator?.files() ?? []) {
|
|
8223
|
+
if (!trackPublishedRefs || typeof file.content !== "string") continue;
|
|
8224
|
+
const original = /^src\/mdx\/migrated\/[^/]+\/source\/(.+)$/.exec(file.path)?.[1];
|
|
8225
|
+
addPathReferences(file.content, original === void 0 ? "" : posix3.dirname(original).replace(/^\.$/, ""), publishedExact, publishedLoose);
|
|
8226
|
+
}
|
|
8227
|
+
const failClosedAssets = trackPublishedRefs;
|
|
8228
|
+
const isPublicReachable = (assetPath) => referencedAssetPaths.has(assetPath) || publishedExact.has(assetPath);
|
|
8229
|
+
const isQuarantinedAsset = (assetPath) => (withheldAssetPaths.has(assetPath) || failClosedAssets && !siteAssetPaths.has(assetPath)) && platform === "mintlify" && !(isPublicReachable(assetPath) && !(failClosedAssets && (destinationCounts.get(assetPath) ?? 0) > 1));
|
|
8230
|
+
let withheldAssetCount = 0;
|
|
8231
|
+
let unreferencedAssetCount = 0;
|
|
7500
8232
|
let totalAssetBytes = 0;
|
|
8233
|
+
const overBudgetAssets = [];
|
|
8234
|
+
let overBudgetReferenced = 0;
|
|
8235
|
+
const lfsPointerAssets = [];
|
|
8236
|
+
const ambiguouslyNamedAssets = [];
|
|
7501
8237
|
for (const { file, assetPath, size } of orderedAssetCandidates) {
|
|
7502
8238
|
if (size > MAX_ASSET_BYTES || totalAssetBytes + size > MAX_TOTAL_ASSET_BYTES) {
|
|
7503
|
-
|
|
7504
|
-
|
|
7505
|
-
code: "limit-reached",
|
|
7506
|
-
message: "This file was not copied because it is larger than 25 MB or the copied files would exceed 500 MB in total. Add it to public/ manually" + (referencingPages ? `; it is used by ${[...referencingPages].slice(0, 3).join(", ")}${referencingPages.size > 3 ? `, and ${referencingPages.size - 3} more` : ""}.` : "."),
|
|
7507
|
-
source: file.relativePath
|
|
7508
|
-
});
|
|
8239
|
+
overBudgetAssets.push(file.relativePath);
|
|
8240
|
+
if (referencedAssetPaths.has(assetPath)) overBudgetReferenced++;
|
|
7509
8241
|
continue;
|
|
7510
8242
|
}
|
|
7511
8243
|
const content = readFileSync5(file.absolutePath);
|
|
7512
8244
|
if (isGitLfsPointer(content)) {
|
|
7513
|
-
|
|
7514
|
-
|
|
7515
|
-
|
|
7516
|
-
|
|
7517
|
-
});
|
|
8245
|
+
lfsPointerAssets.push(file.relativePath);
|
|
8246
|
+
continue;
|
|
8247
|
+
}
|
|
8248
|
+
if (isQuarantinedAsset(assetPath)) {
|
|
8249
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${assetPath}`, content });
|
|
8250
|
+
if (looselyNamed(assetPath, publishedLoose) || (destinationCounts.get(assetPath) ?? 0) > 1) ambiguouslyNamedAssets.push(assetPath);
|
|
8251
|
+
if (withheldAssetPaths.has(assetPath)) withheldAssetCount++;
|
|
8252
|
+
else unreferencedAssetCount++;
|
|
7518
8253
|
continue;
|
|
7519
8254
|
}
|
|
7520
8255
|
assets.push({ path: assetPath, content });
|
|
7521
8256
|
totalAssetBytes += size;
|
|
7522
8257
|
}
|
|
7523
|
-
if (
|
|
7524
|
-
warnings.push({
|
|
8258
|
+
if (ambiguouslyNamedAssets.length > 0) {
|
|
8259
|
+
warnings.push({
|
|
8260
|
+
code: "gated-page",
|
|
8261
|
+
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.`
|
|
8262
|
+
});
|
|
8263
|
+
}
|
|
8264
|
+
if (platform === "mintlify") {
|
|
8265
|
+
const copiedSite = assets.flatMap((asset) => siteAssetPaths.has(asset.path) ? [{ path: asset.path, kind: siteAssetPaths.get(asset.path) }] : []);
|
|
8266
|
+
const scripts = copiedSite.filter((entry) => entry.kind === "script");
|
|
8267
|
+
if (scripts.length > 0) {
|
|
8268
|
+
docsConfig = {
|
|
8269
|
+
...docsConfig,
|
|
8270
|
+
customScripts: [...docsConfig.customScripts ?? [], ...scripts.map((entry) => ({ src: `/${entry.path.split("/").map(encodeURIComponent).join("/")}`, strategy: "afterInteractive" }))]
|
|
8271
|
+
};
|
|
8272
|
+
warnings.push({
|
|
8273
|
+
code: "unsupported-config",
|
|
8274
|
+
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.`
|
|
8275
|
+
});
|
|
8276
|
+
}
|
|
8277
|
+
for (const entry of copiedSite.filter((item) => item.kind === "style")) {
|
|
8278
|
+
warnings.push({
|
|
8279
|
+
code: "unsupported-config",
|
|
8280
|
+
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.`,
|
|
8281
|
+
source: entry.path
|
|
8282
|
+
});
|
|
8283
|
+
}
|
|
8284
|
+
for (const font of mintlifyFonts) {
|
|
8285
|
+
const copiedPath = font.path ? fontAssetByRelative.get(font.path) : void 0;
|
|
8286
|
+
const copied = copiedPath !== void 0 && assets.some((asset) => asset.path === copiedPath);
|
|
8287
|
+
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;
|
|
8288
|
+
if (detail === null) continue;
|
|
8289
|
+
warnings.push({
|
|
8290
|
+
code: "unsupported-config",
|
|
8291
|
+
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.`
|
|
8292
|
+
});
|
|
8293
|
+
}
|
|
8294
|
+
}
|
|
8295
|
+
if (overBudgetAssets.length > 0) {
|
|
8296
|
+
warnings.push({
|
|
8297
|
+
code: "limit-reached",
|
|
8298
|
+
message: `${overBudgetAssets.length} asset file${overBudgetAssets.length === 1 ? " was" : "s were"} not copied because files over ${MAX_ASSET_BYTES / 1e6} MB, or beyond ${MAX_TOTAL_ASSET_BYTES / 1e6} MB in total, are skipped: ${listAssetPaths(overBudgetAssets)}. ` + (overBudgetReferenced > 0 ? `${overBudgetReferenced} of them ${overBudgetReferenced === 1 ? "is" : "are"} used by pages, so those images will be broken until you copy ${overBudgetAssets.length === 1 ? "it" : "them"}. ` : "") + `Copy ${overBudgetAssets.length === 1 ? "it" : "them"} into public/ manually.`
|
|
8299
|
+
});
|
|
8300
|
+
}
|
|
8301
|
+
if (lfsPointerAssets.length > 0) {
|
|
8302
|
+
warnings.push({
|
|
8303
|
+
code: "unsupported-config",
|
|
8304
|
+
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.`
|
|
8305
|
+
});
|
|
8306
|
+
}
|
|
8307
|
+
if (!discoveryBudgetApplied && files.length >= sourceBudget) {
|
|
8308
|
+
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.` });
|
|
7525
8309
|
}
|
|
7526
8310
|
if (platform === "docusaurus") {
|
|
7527
8311
|
const projected = projectDocusaurusNavigation({
|
|
@@ -7608,8 +8392,8 @@ function migrateRepository(options) {
|
|
|
7608
8392
|
}
|
|
7609
8393
|
specFilenameSources.set(filename, spec.absolutePath);
|
|
7610
8394
|
const specContent = readFileSync5(spec.absolutePath);
|
|
7611
|
-
if (!assets.some((asset) => asset.path === filename)) {
|
|
7612
|
-
assets.push(
|
|
8395
|
+
if (!assets.some((asset) => asset.path === specAssetPath(filename))) {
|
|
8396
|
+
assets.push(specAsset(filename, specContent));
|
|
7613
8397
|
}
|
|
7614
8398
|
resolvedSpecs.push({ filename, tabLabel: section.tabLabel, content: specContent, routeSegments: section.routeSegments ?? [] });
|
|
7615
8399
|
continue;
|
|
@@ -7648,11 +8432,19 @@ function migrateRepository(options) {
|
|
|
7648
8432
|
rewriteApiLinksInPages(pages, operationLinks, prefixLandings, warnings);
|
|
7649
8433
|
}
|
|
7650
8434
|
} else if (platform === "mintlify") {
|
|
8435
|
+
const warnSharedSpec = (path) => {
|
|
8436
|
+
warnings.push({
|
|
8437
|
+
code: "gated-page",
|
|
8438
|
+
message: `OpenAPI spec ${path} is shared with access-restricted pages and is published; it may describe restricted endpoints. Review it before publishing.`,
|
|
8439
|
+
source: path
|
|
8440
|
+
});
|
|
8441
|
+
};
|
|
7651
8442
|
const resolvedSpecs = resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteApiSpecs);
|
|
7652
8443
|
for (const spec of resolvedSpecs) {
|
|
7653
|
-
if (!assets.some((asset) => asset.path === spec.filename)) {
|
|
7654
|
-
assets.push(
|
|
8444
|
+
if (!assets.some((asset) => asset.path === specAssetPath(spec.filename))) {
|
|
8445
|
+
assets.push(specAsset(spec.filename, spec.content));
|
|
7655
8446
|
}
|
|
8447
|
+
if (withheldSpecRefs.some((ref) => specRefMatches(ref, spec.sourcePath, spec.filename, true))) warnSharedSpec(spec.sourcePath);
|
|
7656
8448
|
}
|
|
7657
8449
|
if (resolvedSpecs.length > 0) {
|
|
7658
8450
|
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs, warnings);
|
|
@@ -7665,10 +8457,17 @@ function migrateRepository(options) {
|
|
|
7665
8457
|
const fallback = findOpenApi(files);
|
|
7666
8458
|
if (fallback) {
|
|
7667
8459
|
const filename = basename3(fallback.relativePath);
|
|
7668
|
-
|
|
7669
|
-
|
|
8460
|
+
const matches = (ref) => specRefMatches(ref, fallback.relativePath, filename, true);
|
|
8461
|
+
if (withheldSpecRefs.some(matches) && !publishedSpecRefs.some(matches)) {
|
|
8462
|
+
quarantinedFiles.push({ path: `${QUARANTINE_DIRECTORY}/assets/${specAssetPath(filename)}`, content: readFileSync5(fallback.absolutePath) });
|
|
8463
|
+
withheldAssetCount++;
|
|
8464
|
+
} else {
|
|
8465
|
+
if (!assets.some((asset) => asset.path === specAssetPath(filename))) {
|
|
8466
|
+
assets.push(specAsset(filename, readFileSync5(fallback.absolutePath)));
|
|
8467
|
+
}
|
|
8468
|
+
docsConfig = injectOpenApiSpecs(docsConfig, [{ filename }]);
|
|
8469
|
+
if (withheldSpecRefs.some(matches)) warnSharedSpec(fallback.relativePath);
|
|
7670
8470
|
}
|
|
7671
|
-
docsConfig = injectOpenApiSpecs(docsConfig, [{ filename }]);
|
|
7672
8471
|
}
|
|
7673
8472
|
}
|
|
7674
8473
|
}
|
|
@@ -7979,10 +8778,9 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
7979
8778
|
docsConfig = { ...docsConfig, navbar: { ...docsConfig.navbar, logo: null } };
|
|
7980
8779
|
}
|
|
7981
8780
|
if (mintlifyConfig) {
|
|
7982
|
-
const appearance = mintlifyConfig
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
docsConfig = { ...docsConfig, appearance: { ...docsConfig.appearance, default: defaultMode } };
|
|
8781
|
+
const appearance = mintlifyAppearance(mintlifyConfig, warnings);
|
|
8782
|
+
if (Object.keys(appearance).length > 0) {
|
|
8783
|
+
docsConfig = { ...docsConfig, appearance: { ...docsConfig.appearance, ...appearance } };
|
|
7986
8784
|
}
|
|
7987
8785
|
const background = mintlifyConfig.background && typeof mintlifyConfig.background === "object" ? mintlifyConfig.background : null;
|
|
7988
8786
|
if (background) {
|
|
@@ -8015,6 +8813,25 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
8015
8813
|
code: "unsupported-config",
|
|
8016
8814
|
message: `Brand asset(s) were referenced but not imported: ${[...new Set(missingBrand)].join(", ")}. Add these files under public/ or update docs.json.`
|
|
8017
8815
|
});
|
|
8816
|
+
if (platform === "mintlify") {
|
|
8817
|
+
docsConfig = { ...docsConfig, markdown: { enabled: true } };
|
|
8818
|
+
if (quarantinedPageCount > 0 || withheldAssetCount > 0 || unreferencedAssetCount > 0) {
|
|
8819
|
+
const assetNote = withheldAssetCount > 0 ? `${withheldAssetCount} file(s) used only by access-restricted pages were kept out of public/ and saved under ${QUARANTINE_DIRECTORY}/assets/. ` : "";
|
|
8820
|
+
const unreferencedReason = [
|
|
8821
|
+
...hasWithheldContent ? ["this site has access-restricted content"] : [],
|
|
8822
|
+
...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"] : []
|
|
8823
|
+
].join(" and ");
|
|
8824
|
+
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. ` : "";
|
|
8825
|
+
warnings.push({
|
|
8826
|
+
code: "gated-page",
|
|
8827
|
+
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."
|
|
8828
|
+
});
|
|
8829
|
+
}
|
|
8830
|
+
warnings.push({
|
|
8831
|
+
code: "gated-page",
|
|
8832
|
+
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."
|
|
8833
|
+
});
|
|
8834
|
+
}
|
|
8018
8835
|
if (fernRawConfig?.logo && !logoLight && !logo.light) warnings.push({
|
|
8019
8836
|
code: "unsupported-config",
|
|
8020
8837
|
message: "Fern supplied a logo through its global theme without a local asset; add a logo path to docs.json after import."
|
|
@@ -8027,6 +8844,8 @@ ${reasons.map((reason) => `- ${reason}`).join("\n")}` : "No importable Markdown
|
|
|
8027
8844
|
assets,
|
|
8028
8845
|
...remoteApiSpecs.length > 0 ? { remoteApiSpecs } : {},
|
|
8029
8846
|
...componentMigrator ? { componentFiles: componentMigrator.files() } : {},
|
|
8847
|
+
...quarantinedFiles.length > 0 ? { quarantinedFiles } : {},
|
|
8848
|
+
...droppedGatedFiles.length > 0 ? { droppedGatedPages: droppedGatedFiles.length } : {},
|
|
8030
8849
|
docsConfig,
|
|
8031
8850
|
...mintlifyConfig ? {
|
|
8032
8851
|
site: {
|
|
@@ -8157,8 +8976,9 @@ function renderMigrationFiles(bundle, options = {}) {
|
|
|
8157
8976
|
path: `src/content/${page.id}.mdx`,
|
|
8158
8977
|
content: renderPage(bundle, page)
|
|
8159
8978
|
})),
|
|
8160
|
-
...bundle.assets.map((asset) => ({ path: `public/${asset.path}`, content: asset.content })),
|
|
8979
|
+
...bundle.assets.map((asset) => ({ path: asset.projectRelative ? asset.path : `public/${asset.path}`, content: asset.content })),
|
|
8161
8980
|
...(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]),
|
|
8981
|
+
...bundle.quarantinedFiles ?? [],
|
|
8162
8982
|
{ path: "docs.json", content: `${JSON.stringify(config, null, 2)}
|
|
8163
8983
|
` }
|
|
8164
8984
|
];
|
|
@@ -9899,10 +10719,10 @@ async function hydrateRemoteApiSpecs(bundle, fetcher) {
|
|
|
9899
10719
|
const body = await downloadSpec(reference.url, fetcher);
|
|
9900
10720
|
const operations = parseOpenApi(body, extension);
|
|
9901
10721
|
const filename = `openapi-${createHash2("sha256").update(reference.url).digest("hex").slice(0, 12)}.${extension}`;
|
|
9902
|
-
assets.push({ path: filename
|
|
10722
|
+
assets.push({ path: `openapi/${filename}`, content: body, projectRelative: true });
|
|
9903
10723
|
const tab = reference.tabLabel ? tabs.find((candidate) => candidate.tab === reference.tabLabel) : tabs.find((candidate) => candidate.tab.toLowerCase().includes("api"));
|
|
9904
|
-
if (tab) tab.api = { source:
|
|
9905
|
-
else tabs
|
|
10724
|
+
if (tab) tab.api = { source: `openapi/${filename}` };
|
|
10725
|
+
else insertApiTab(tabs, { tab: reference.tabLabel ?? "API Reference", ...reference.icon ? { icon: reference.icon } : {}, ...reference.hidden ? { hidden: true } : {}, api: { source: `openapi/${filename}` } }, reference.parentTab);
|
|
9906
10726
|
const routes = /* @__PURE__ */ new Map();
|
|
9907
10727
|
for (const operation of operations) {
|
|
9908
10728
|
const key = `${slug(operation.tag)}/${slug(operation.summary)}`;
|