@thallylabs/migrate 0.2.10 → 0.2.11
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 +19 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +579 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -31,6 +31,20 @@ import { unified as unified2 } from "unified";
|
|
|
31
31
|
|
|
32
32
|
// src/frontmatter.ts
|
|
33
33
|
import { parse as parseYaml } from "yaml";
|
|
34
|
+
function salvageFrontmatterLines(matter) {
|
|
35
|
+
const data = {};
|
|
36
|
+
for (const line of matter.split(/\r?\n/)) {
|
|
37
|
+
if (!/^[A-Za-z_][\w-]*:\s?/.test(line)) continue;
|
|
38
|
+
try {
|
|
39
|
+
const parsedLine = parseYaml(line);
|
|
40
|
+
if (parsedLine && typeof parsedLine === "object" && !Array.isArray(parsedLine)) {
|
|
41
|
+
Object.assign(data, parsedLine);
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
34
48
|
function parseFrontmatter(raw) {
|
|
35
49
|
const source = raw.charCodeAt(0) === 65279 ? raw.slice(1) : raw;
|
|
36
50
|
const opening = /^---([^\r\n]*)\r?\n/.exec(source);
|
|
@@ -45,11 +59,19 @@ function parseFrontmatter(raw) {
|
|
|
45
59
|
if (matter.trim() === "" || !["", "yaml", "yml"].includes(language)) {
|
|
46
60
|
return { content, data: {} };
|
|
47
61
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
62
|
+
try {
|
|
63
|
+
const parsed = parseYaml(matter);
|
|
64
|
+
return {
|
|
65
|
+
content,
|
|
66
|
+
data: parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {}
|
|
67
|
+
};
|
|
68
|
+
} catch (err) {
|
|
69
|
+
return {
|
|
70
|
+
content,
|
|
71
|
+
data: salvageFrontmatterLines(matter),
|
|
72
|
+
error: err instanceof Error ? err.message : String(err)
|
|
73
|
+
};
|
|
74
|
+
}
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
// src/mdx.ts
|
|
@@ -233,8 +255,9 @@ function unwrapDocusaurusMdxCodeBlocks(body) {
|
|
|
233
255
|
}
|
|
234
256
|
return isMdxCodeBlock ? body : output.join("\n");
|
|
235
257
|
}
|
|
236
|
-
function normalizeExplicitHeadingIds(
|
|
237
|
-
|
|
258
|
+
function normalizeExplicitHeadingIds(raw) {
|
|
259
|
+
const { front, body } = splitFrontmatterBlock(raw);
|
|
260
|
+
return front + replaceOutsideCode(body, (segment) => segment.split("\n").map((line) => {
|
|
238
261
|
if (!/^[ \t]{0,3}#{1,6}[ \t]+/.test(line)) return line;
|
|
239
262
|
const trimmed = line.trimEnd();
|
|
240
263
|
if (trimmed.endsWith("}")) {
|
|
@@ -276,6 +299,7 @@ function removeGlobalDocusaurusImports(body) {
|
|
|
276
299
|
}
|
|
277
300
|
var JS_LITERAL_KEYWORDS = /* @__PURE__ */ new Set(["true", "false", "null", "undefined"]);
|
|
278
301
|
var BARE_IDENTIFIER_PATH = /^[\p{ID_Start}$_][\p{ID_Continue}$]*(?:\.[\p{ID_Start}$_][\p{ID_Continue}$]*)*$/u;
|
|
302
|
+
var BARE_OBJECT_SHORTHAND = /^\{\s*([\p{ID_Start}$_][\p{ID_Continue}$]*)\s*\}$/u;
|
|
279
303
|
var SAFE_BUILTIN_ROOTS = /* @__PURE__ */ new Set([
|
|
280
304
|
"Math",
|
|
281
305
|
"JSON",
|
|
@@ -500,12 +524,13 @@ function escapeFernLiteralBraces(raw) {
|
|
|
500
524
|
const end = node.position?.end.offset;
|
|
501
525
|
const root2 = value.split(".")[0];
|
|
502
526
|
const safeBuiltinPath = value.includes(".") && SAFE_BUILTIN_ROOTS.has(root2);
|
|
503
|
-
const
|
|
527
|
+
const isBareIdentifier = BARE_IDENTIFIER_PATH.test(value) && !JS_LITERAL_KEYWORDS.has(value) && !declared.has(root2) && !safeBuiltinPath;
|
|
528
|
+
const shorthandMatch = value.match(BARE_OBJECT_SHORTHAND);
|
|
529
|
+
const isDoubleBraceTemplate = shorthandMatch !== null && !declared.has(shorthandMatch[1]);
|
|
504
530
|
const isIssueList = /^[A-Za-z][A-Za-z0-9]*-\d+(?:\s*,\s*[A-Za-z][A-Za-z0-9]*-\d+)*$/.test(value);
|
|
505
|
-
if ((
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
edits.push({ start, end, value: `\\{${value}\\}` });
|
|
531
|
+
if ((isBareIdentifier || isDoubleBraceTemplate || isIssueList) && start !== void 0 && end !== void 0) {
|
|
532
|
+
const rawSpan = body.slice(start, end);
|
|
533
|
+
edits.push({ start, end, value: rawSpan.replace(/\\/g, "\\\\").replace(/[{}]/g, "\\$&") });
|
|
509
534
|
}
|
|
510
535
|
return;
|
|
511
536
|
}
|
|
@@ -1153,7 +1178,8 @@ function parseMarkdownPage(input) {
|
|
|
1153
1178
|
noindex: parsed.data.noindex === true || parsed.data.noindex === "true" ? true : void 0,
|
|
1154
1179
|
openapi: typeof parsed.data.openapi === "string" ? parsed.data.openapi.trim() : void 0,
|
|
1155
1180
|
body,
|
|
1156
|
-
source: input.source
|
|
1181
|
+
source: input.source,
|
|
1182
|
+
...parsed.error ? { frontmatterError: parsed.error } : {}
|
|
1157
1183
|
};
|
|
1158
1184
|
}
|
|
1159
1185
|
|
|
@@ -1206,6 +1232,11 @@ function mintlifyLocalizedReference(value, locale, defaultPageIds = /* @__PURE__
|
|
|
1206
1232
|
const candidateId = pageIdFromReference(candidate, true);
|
|
1207
1233
|
if (candidateId && defaultPageIds.has(candidateId)) return candidate;
|
|
1208
1234
|
}
|
|
1235
|
+
const segments = reference.split("/");
|
|
1236
|
+
const localeIndex = segments.findIndex((segment) => segment.toLowerCase() === locale.toLowerCase());
|
|
1237
|
+
if (localeIndex > 0) {
|
|
1238
|
+
return [...segments.slice(0, localeIndex), ...segments.slice(localeIndex + 1)].join("/");
|
|
1239
|
+
}
|
|
1209
1240
|
return value;
|
|
1210
1241
|
}
|
|
1211
1242
|
function resolveWithin(root, candidate) {
|
|
@@ -1664,6 +1695,19 @@ function declarationsReferenceBrowserGlobal(body) {
|
|
|
1664
1695
|
}
|
|
1665
1696
|
return found;
|
|
1666
1697
|
}
|
|
1698
|
+
function neutralizeUnresolvableImportsWithoutAst(content, currentFile, warn) {
|
|
1699
|
+
const importPattern = /^import\s+(?:\{\s*(?:default\s+as\s+)?([A-Z][A-Za-z0-9_]*)\s*\}|([A-Z][A-Za-z0-9_]*))\s+from\s+(['"])((?:@site\/|\.\.?\/|\/)[^'"]+)\3\s*;?[ \t]*$/gm;
|
|
1700
|
+
const dropped = [];
|
|
1701
|
+
let result = content.replace(importPattern, (_match, namedComponent, defaultComponent, _quote, specifier) => {
|
|
1702
|
+
dropped.push({ name: namedComponent ?? defaultComponent, specifier });
|
|
1703
|
+
return "";
|
|
1704
|
+
});
|
|
1705
|
+
for (const { name, specifier } of dropped) {
|
|
1706
|
+
warn(`Component import ${JSON.stringify(specifier)} could not be resolved because this page has invalid MDX (see the previous warning), so the import and its usage were removed to keep the site building.`, currentFile);
|
|
1707
|
+
result = result.replace(new RegExp(`<${name}(?:\\s[^>]*)?/>`, "g"), `{/* Removed <${name}>: unresolved during fallback migration */}`).replace(new RegExp(`<${name}(?:\\s[^>]*)?>[\\s\\S]*?<\\/${name}>`, "g"), `{/* Removed <${name}>: unresolved during fallback migration */}`);
|
|
1708
|
+
}
|
|
1709
|
+
return result;
|
|
1710
|
+
}
|
|
1667
1711
|
function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIdentity) {
|
|
1668
1712
|
const root = resolve2(siteRoot);
|
|
1669
1713
|
const confined = resolve2(confinementRoot);
|
|
@@ -1858,16 +1902,17 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1858
1902
|
function transform(raw, currentFile) {
|
|
1859
1903
|
const parsedFrontmatter = parseFrontmatter(raw).content;
|
|
1860
1904
|
const frontmatter = raw.slice(0, raw.length - parsedFrontmatter.length);
|
|
1861
|
-
const content = normalizeIndentedFences(normalizeHtmlComments(parsedFrontmatter));
|
|
1905
|
+
const content = normalizeExplicitHeadingIds(normalizeIndentedFences(normalizeHtmlComments(parsedFrontmatter)));
|
|
1862
1906
|
let tree;
|
|
1863
1907
|
try {
|
|
1864
1908
|
tree = parser.parse(content);
|
|
1865
1909
|
} catch {
|
|
1866
1910
|
warn("Custom component analysis could not parse this MDX; its source was preserved for manual migration.", currentFile);
|
|
1867
|
-
return
|
|
1911
|
+
return frontmatter + neutralizeUnresolvableImportsWithoutAst(content, currentFile, warn);
|
|
1868
1912
|
}
|
|
1869
1913
|
const aliases = /* @__PURE__ */ new Map();
|
|
1870
1914
|
const unsupportedImports = /* @__PURE__ */ new Map();
|
|
1915
|
+
const droppedMdxDataBindings = /* @__PURE__ */ new Set();
|
|
1871
1916
|
const edits = [];
|
|
1872
1917
|
const declarations = [];
|
|
1873
1918
|
const moduleImports = [];
|
|
@@ -1967,14 +2012,31 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
1967
2012
|
}
|
|
1968
2013
|
continue;
|
|
1969
2014
|
}
|
|
1970
|
-
if (/\.mdx?$/.test(specifier))
|
|
2015
|
+
if (/\.mdx?$/.test(specifier)) {
|
|
2016
|
+
if (bindings.length > 0 && bindings.every((binding) => !/^[A-Z]/.test(binding.local))) {
|
|
2017
|
+
warn(`MDX named import of ${JSON.stringify(rawSpecifier)} isn't a component and has no equivalent in the migrated site (for example Docusaurus's generated 'toc' export), so it was removed, along with any '...${bindings.map((binding) => binding.local).join(", '...")}' spread of it.`, currentFile);
|
|
2018
|
+
edits.push({ start: node.position.start.offset + statement.getStart(ast), end: node.position.start.offset + statement.end, value: "" });
|
|
2019
|
+
for (const binding of bindings) droppedMdxDataBindings.add(binding.local);
|
|
2020
|
+
}
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
1971
2023
|
if (bindings.length && RESCUABLE_ASSET_EXTENSIONS.has(extname(specifier).toLowerCase()) && !bindings.some((binding) => usedAsJsxTagName(binding.local))) {
|
|
1972
2024
|
try {
|
|
1973
2025
|
const href = copyPublicAsset(resolveDependency(specifier, currentFile));
|
|
1974
2026
|
edits.push({
|
|
1975
2027
|
start: node.position.start.offset + statement.getStart(ast),
|
|
1976
2028
|
end: node.position.start.offset + statement.end,
|
|
1977
|
-
|
|
2029
|
+
// `export`, not a bare `const`: this replaces an `import`
|
|
2030
|
+
// statement in place, and the final MDX compile (a fresh
|
|
2031
|
+
// parse of this text, not a reuse of this pass's own AST)
|
|
2032
|
+
// only recognizes a top-level JS block as ESM — evaluated and
|
|
2033
|
+
// its bindings put in scope — when it starts with `import` or
|
|
2034
|
+
// `export`. A bare `const` there is just prose to that parser
|
|
2035
|
+
// and renders as literal paragraph text instead, leaving
|
|
2036
|
+
// every reference to this binding a `ReferenceError` at
|
|
2037
|
+
// render (reproduced against hasura/graphql-engine's
|
|
2038
|
+
// databases/overview.mdx, which binds asset URLs this way).
|
|
2039
|
+
value: bindings.map((binding) => `export const ${binding.local} = ${JSON.stringify(href)};`).join("\n")
|
|
1978
2040
|
});
|
|
1979
2041
|
warn(`Asset import ${JSON.stringify(rawSpecifier)} was copied to ${href} and bound to that URL instead of importing it as a component.`, currentFile);
|
|
1980
2042
|
continue;
|
|
@@ -2042,7 +2104,9 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2042
2104
|
edits.push({
|
|
2043
2105
|
start: importStart,
|
|
2044
2106
|
end: importEnd,
|
|
2045
|
-
|
|
2107
|
+
// See the identical `export const` note on the other asset
|
|
2108
|
+
// rescue above — same reason.
|
|
2109
|
+
value: bindings.map((binding) => `export const ${binding.local} = ${JSON.stringify(rescuedAssetHref)};`).join("\n")
|
|
2046
2110
|
});
|
|
2047
2111
|
warn(`Asset import ${JSON.stringify(rawSpecifier)} could not be copied as a component; it was copied to ${rescuedAssetHref} and bound to that URL instead.`, currentFile);
|
|
2048
2112
|
} else {
|
|
@@ -2052,6 +2116,27 @@ function createComponentMigrator(siteRoot, confinementRoot, warnings, sourceIden
|
|
|
2052
2116
|
}
|
|
2053
2117
|
}
|
|
2054
2118
|
}
|
|
2119
|
+
if (droppedMdxDataBindings.size > 0) {
|
|
2120
|
+
for (const node of esmNodes) {
|
|
2121
|
+
let stripDroppedSpreads2 = function(inner) {
|
|
2122
|
+
if (ts.isSpreadElement(inner) && ts.isIdentifier(inner.expression) && droppedMdxDataBindings.has(inner.expression.text)) {
|
|
2123
|
+
const start = nodeStart + inner.getStart(ast);
|
|
2124
|
+
let end = nodeStart + inner.end;
|
|
2125
|
+
const following = nodeText.slice(inner.end).match(/^\s*,/);
|
|
2126
|
+
if (following) end += following[0].length;
|
|
2127
|
+
edits.push({ start, end, value: "" });
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
ts.forEachChild(inner, stripDroppedSpreads2);
|
|
2131
|
+
};
|
|
2132
|
+
var stripDroppedSpreads = stripDroppedSpreads2;
|
|
2133
|
+
if (node.value === void 0 || node.position?.start.offset === void 0) continue;
|
|
2134
|
+
const nodeStart = node.position.start.offset;
|
|
2135
|
+
const nodeText = node.value;
|
|
2136
|
+
const ast = sourceFile(nodeText, "inline.tsx");
|
|
2137
|
+
for (const statement of ast.statements) stripDroppedSpreads2(statement);
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2055
2140
|
walk(tree, (node) => {
|
|
2056
2141
|
for (const attribute of node.attributes ?? []) {
|
|
2057
2142
|
if (attribute.type !== "mdxJsxAttribute" || !attribute.value || typeof attribute.value !== "object") continue;
|
|
@@ -2315,7 +2400,7 @@ import { parse as parseYaml2 } from "yaml";
|
|
|
2315
2400
|
// src/navigation.ts
|
|
2316
2401
|
import { existsSync as existsSync2, lstatSync as lstatSync2, readFileSync as readFileSync2, realpathSync } from "fs";
|
|
2317
2402
|
import { dirname as dirname2, extname as extname2, relative as relative3 } from "path";
|
|
2318
|
-
var MAX_MINTLIFY_CONFIG_BYTES =
|
|
2403
|
+
var MAX_MINTLIFY_CONFIG_BYTES = 2e7;
|
|
2319
2404
|
function addMintlifyDirectoryRedirects(config, pages) {
|
|
2320
2405
|
const pageIds = new Set(pages.map((page) => page.id));
|
|
2321
2406
|
const redirects = [...config.redirects ?? []];
|
|
@@ -2363,15 +2448,10 @@ function addMintlifyDirectoryRedirects(config, pages) {
|
|
|
2363
2448
|
}
|
|
2364
2449
|
}
|
|
2365
2450
|
}
|
|
2366
|
-
for (const
|
|
2367
|
-
|
|
2368
|
-
if (!destination.startsWith("/") || destination.startsWith("//") || /[:*()[\]{}]/.test(destination)) continue;
|
|
2369
|
-
const directory = trimEdgeSlashes(destination);
|
|
2370
|
-
if (!directory || pageIds.has(directory)) continue;
|
|
2451
|
+
for (const [directory, landing] of firstDescendantByDirectory) {
|
|
2452
|
+
if (pageIds.has(directory)) continue;
|
|
2371
2453
|
const source = `/${directory}`;
|
|
2372
2454
|
if (hasAuthoredRedirect(source)) continue;
|
|
2373
|
-
const landing = firstDescendantByDirectory.get(directory);
|
|
2374
|
-
if (!landing) continue;
|
|
2375
2455
|
redirects.push({ source, destination: `/${landing.id}`, permanent: false });
|
|
2376
2456
|
redirectSources.add(source);
|
|
2377
2457
|
}
|
|
@@ -2422,6 +2502,18 @@ var LANGUAGE_LABELS = {
|
|
|
2422
2502
|
function objectValue(value) {
|
|
2423
2503
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
2424
2504
|
}
|
|
2505
|
+
function withFlattenedProductGroups(container) {
|
|
2506
|
+
if (!Array.isArray(container.productGroups)) return container;
|
|
2507
|
+
const flattened = container.productGroups.flatMap((entry) => {
|
|
2508
|
+
const group = objectValue(entry);
|
|
2509
|
+
return group && Array.isArray(group.products) ? group.products : [];
|
|
2510
|
+
});
|
|
2511
|
+
if (flattened.length === 0) return container;
|
|
2512
|
+
return {
|
|
2513
|
+
...container,
|
|
2514
|
+
products: [...Array.isArray(container.products) ? container.products : [], ...flattened]
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2425
2517
|
function projectedHref(value) {
|
|
2426
2518
|
if (typeof value !== "string") return void 0;
|
|
2427
2519
|
const href = value.trim();
|
|
@@ -2509,12 +2601,23 @@ function mintlifyNavigationApiReferences(config) {
|
|
|
2509
2601
|
const references = [];
|
|
2510
2602
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus", "languages"];
|
|
2511
2603
|
const nestedKeys = ["groups", "pages"];
|
|
2512
|
-
function visit(
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2604
|
+
function visit(rawNode, tabLabel) {
|
|
2605
|
+
const node = withFlattenedProductGroups(rawNode);
|
|
2606
|
+
for (const kind of ["openapi", "asyncapi"]) {
|
|
2607
|
+
const value = node[kind];
|
|
2608
|
+
if (typeof value === "string" && value.trim()) {
|
|
2609
|
+
references.push({ value: value.trim(), kind, tabLabel });
|
|
2610
|
+
continue;
|
|
2611
|
+
}
|
|
2612
|
+
const object = objectValue(value);
|
|
2613
|
+
if (object && typeof object.source === "string" && object.source.trim()) {
|
|
2614
|
+
references.push({
|
|
2615
|
+
value: object.source.trim(),
|
|
2616
|
+
kind,
|
|
2617
|
+
tabLabel,
|
|
2618
|
+
...typeof object.directory === "string" && object.directory.trim() ? { directory: object.directory.trim() } : {}
|
|
2619
|
+
});
|
|
2620
|
+
}
|
|
2518
2621
|
}
|
|
2519
2622
|
for (const key of containerKeys) {
|
|
2520
2623
|
const entries = node[key];
|
|
@@ -2575,8 +2678,19 @@ function registerReference(value, context) {
|
|
|
2575
2678
|
}
|
|
2576
2679
|
return navigationId;
|
|
2577
2680
|
}
|
|
2681
|
+
var OPENAPI_OPERATION_REF = /^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS)\s+\S/;
|
|
2578
2682
|
function convertPage(value, context) {
|
|
2579
|
-
if (typeof value === "string")
|
|
2683
|
+
if (typeof value === "string") {
|
|
2684
|
+
if (OPENAPI_OPERATION_REF.test(value)) {
|
|
2685
|
+
warnOnce(
|
|
2686
|
+
context,
|
|
2687
|
+
"openapi-operation-list",
|
|
2688
|
+
'One or more navigation groups hand-pick or reorder individual OpenAPI operations (e.g. "GET /users"); Thally always renders every operation of a bound spec and has no manual-selection equivalent, so that list was dropped. The operations themselves are still available through the bound API reference.'
|
|
2689
|
+
);
|
|
2690
|
+
return null;
|
|
2691
|
+
}
|
|
2692
|
+
return registerReference(value, context);
|
|
2693
|
+
}
|
|
2580
2694
|
const object = objectValue(value);
|
|
2581
2695
|
if (!object) return null;
|
|
2582
2696
|
if (typeof object.page === "string") return registerReference(object.page, context);
|
|
@@ -2619,8 +2733,9 @@ function convertNavigationValues(values, context) {
|
|
|
2619
2733
|
});
|
|
2620
2734
|
}
|
|
2621
2735
|
function convertContainerToTabs(containerValue, context, fallbackTab, trace, depth = 0) {
|
|
2622
|
-
const
|
|
2623
|
-
if (!
|
|
2736
|
+
const rawContainer = objectValue(containerValue);
|
|
2737
|
+
if (!rawContainer) return [];
|
|
2738
|
+
const container = withFlattenedProductGroups(rawContainer);
|
|
2624
2739
|
const containerKeys = ["tabs", "anchors", "products", "dropdowns", "versions", "menus"];
|
|
2625
2740
|
for (const key of containerKeys) {
|
|
2626
2741
|
if (!Array.isArray(container[key])) continue;
|
|
@@ -2842,6 +2957,47 @@ function isRedirectPathSafe(rawSource, rawDestination) {
|
|
|
2842
2957
|
}
|
|
2843
2958
|
return true;
|
|
2844
2959
|
}
|
|
2960
|
+
function mintlifyDefaultVersionPrefixes(config) {
|
|
2961
|
+
const navigation = objectValue(config.navigation) ?? config;
|
|
2962
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
2963
|
+
function collect(container) {
|
|
2964
|
+
if (Array.isArray(container.versions)) {
|
|
2965
|
+
const entries = container.versions.map((value) => objectValue(value)).filter((value) => value !== null);
|
|
2966
|
+
const defaultEntry = entries.find((entry) => entry.default === true) ?? entries[0];
|
|
2967
|
+
if (defaultEntry && typeof defaultEntry.version === "string" && defaultEntry.version.trim()) {
|
|
2968
|
+
prefixes.add(defaultEntry.version.trim());
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
if (Array.isArray(container.languages)) {
|
|
2972
|
+
for (const value of container.languages) {
|
|
2973
|
+
const language = objectValue(value);
|
|
2974
|
+
if (language) collect(language);
|
|
2975
|
+
}
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
collect(navigation);
|
|
2979
|
+
return prefixes;
|
|
2980
|
+
}
|
|
2981
|
+
function mintlifyAllVersionPrefixes(config) {
|
|
2982
|
+
const navigation = objectValue(config.navigation) ?? config;
|
|
2983
|
+
const prefixes = /* @__PURE__ */ new Set();
|
|
2984
|
+
function collect(container) {
|
|
2985
|
+
if (Array.isArray(container.versions)) {
|
|
2986
|
+
for (const value of container.versions) {
|
|
2987
|
+
const entry = objectValue(value);
|
|
2988
|
+
if (entry && typeof entry.version === "string" && entry.version.trim()) prefixes.add(entry.version.trim());
|
|
2989
|
+
}
|
|
2990
|
+
}
|
|
2991
|
+
if (Array.isArray(container.languages)) {
|
|
2992
|
+
for (const value of container.languages) {
|
|
2993
|
+
const language = objectValue(value);
|
|
2994
|
+
if (language) collect(language);
|
|
2995
|
+
}
|
|
2996
|
+
}
|
|
2997
|
+
}
|
|
2998
|
+
collect(navigation);
|
|
2999
|
+
return prefixes;
|
|
3000
|
+
}
|
|
2845
3001
|
function projectMintlifyNavigation(config, options = {}) {
|
|
2846
3002
|
const warnings = [];
|
|
2847
3003
|
const references = [];
|
|
@@ -3087,7 +3243,7 @@ function pruneMissingNavigationPages(config, availableIds) {
|
|
|
3087
3243
|
}
|
|
3088
3244
|
|
|
3089
3245
|
// src/docusaurus.ts
|
|
3090
|
-
var MAX_CONFIG_BYTES =
|
|
3246
|
+
var MAX_CONFIG_BYTES = 2e7;
|
|
3091
3247
|
var CATEGORY_FILENAMES = ["_category_.json", "_category_.yml", "_category_.yaml"];
|
|
3092
3248
|
var SIDEBAR_FILENAMES = [
|
|
3093
3249
|
"sidebars.json",
|
|
@@ -3284,7 +3440,8 @@ function rewriteDocusaurusLinks(body, current, descriptors, options = {}) {
|
|
|
3284
3440
|
return options.assetPaths?.has(path.slice(1)) ? target : null;
|
|
3285
3441
|
}
|
|
3286
3442
|
function rewriteTarget(target) {
|
|
3287
|
-
if (!target ||
|
|
3443
|
+
if (!target || /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(target)) return target;
|
|
3444
|
+
if (target.startsWith("#")) return target;
|
|
3288
3445
|
const suffixIndex = target.search(/[?#]/);
|
|
3289
3446
|
const path = suffixIndex >= 0 ? target.slice(0, suffixIndex) : target;
|
|
3290
3447
|
const suffix = suffixIndex >= 0 ? target.slice(suffixIndex) : "";
|
|
@@ -3338,7 +3495,7 @@ function readDocusaurusSiteOrigin(repositoryRoot) {
|
|
|
3338
3495
|
}
|
|
3339
3496
|
function readBoundedText(path) {
|
|
3340
3497
|
if (lstatSync3(path).size > MAX_CONFIG_BYTES) {
|
|
3341
|
-
throw new Error("Docusaurus sidebar config
|
|
3498
|
+
throw new Error("The Docusaurus sidebar config is larger than 20 MB and could not be imported.");
|
|
3342
3499
|
}
|
|
3343
3500
|
return readFileSync3(path, "utf8");
|
|
3344
3501
|
}
|
|
@@ -3970,7 +4127,7 @@ import { existsSync as existsSync4, lstatSync as lstatSync4, readFileSync as rea
|
|
|
3970
4127
|
import { basename as basename2, dirname as dirname3, extname as extname4, relative as relative4 } from "path";
|
|
3971
4128
|
import { load } from "cheerio";
|
|
3972
4129
|
import { parse as parseYaml3 } from "yaml";
|
|
3973
|
-
var MAX_CONFIG_BYTES2 =
|
|
4130
|
+
var MAX_CONFIG_BYTES2 = 2e7;
|
|
3974
4131
|
function announcementMarkdown(message) {
|
|
3975
4132
|
const document = load(message);
|
|
3976
4133
|
document("script, style, iframe, object, embed, svg, math").remove();
|
|
@@ -4005,7 +4162,7 @@ function titleCase3(value) {
|
|
|
4005
4162
|
}
|
|
4006
4163
|
function readBoundedYaml(path) {
|
|
4007
4164
|
if (lstatSync4(path).size > MAX_CONFIG_BYTES2) {
|
|
4008
|
-
throw new Error("Fern config
|
|
4165
|
+
throw new Error("The Fern config is larger than 20 MB and could not be imported.");
|
|
4009
4166
|
}
|
|
4010
4167
|
return parseYaml3(readFileSync4(path, "utf8"));
|
|
4011
4168
|
}
|
|
@@ -4067,7 +4224,7 @@ function registerPageAt(rawPath, base, context, navTitle, hidden = false) {
|
|
|
4067
4224
|
let sourcePath;
|
|
4068
4225
|
try {
|
|
4069
4226
|
const absolute = resolveWithinRoot(
|
|
4070
|
-
context.pathPrefix ?
|
|
4227
|
+
context.pathPrefix ? resolveWithinRoot(context.fernRoot, context.pathPrefix, context.repositoryRoot) : context.fernRoot,
|
|
4071
4228
|
rawPath.trim(),
|
|
4072
4229
|
context.repositoryRoot
|
|
4073
4230
|
);
|
|
@@ -4096,7 +4253,7 @@ function registerFolder(object, parentSegments, context) {
|
|
|
4096
4253
|
let folder;
|
|
4097
4254
|
try {
|
|
4098
4255
|
folder = resolveWithinRoot(
|
|
4099
|
-
context.pathPrefix ?
|
|
4256
|
+
context.pathPrefix ? resolveWithinRoot(context.fernRoot, context.pathPrefix, context.repositoryRoot) : context.fernRoot,
|
|
4100
4257
|
rawPath,
|
|
4101
4258
|
context.repositoryRoot
|
|
4102
4259
|
);
|
|
@@ -4202,10 +4359,11 @@ function convertNode(node, parentSegments, context) {
|
|
|
4202
4359
|
return null;
|
|
4203
4360
|
}
|
|
4204
4361
|
if (typeof object.api === "string") {
|
|
4362
|
+
const ownSegment = segmentFor(object, object.api, context);
|
|
4205
4363
|
context.apiSections.push({
|
|
4206
4364
|
name: typeof object["api-name"] === "string" ? object["api-name"] : object.api,
|
|
4207
4365
|
nameExplicit: typeof object["api-name"] === "string",
|
|
4208
|
-
routeSegments: parentSegments
|
|
4366
|
+
routeSegments: ownSegment ? [...parentSegments, ownSegment] : parentSegments
|
|
4209
4367
|
});
|
|
4210
4368
|
return null;
|
|
4211
4369
|
}
|
|
@@ -4247,7 +4405,7 @@ function resolveVersionedNavigation(config, configDir, fernRoot, context) {
|
|
|
4247
4405
|
}
|
|
4248
4406
|
if (!chosen || typeof chosen.path !== "string") return config;
|
|
4249
4407
|
try {
|
|
4250
|
-
const versionPath = resolveWithinRoot(configDir, chosen.path,
|
|
4408
|
+
const versionPath = resolveWithinRoot(configDir, chosen.path, context.repositoryRoot);
|
|
4251
4409
|
if (!existsSync4(versionPath) || !lstatSync4(versionPath).isFile()) {
|
|
4252
4410
|
warnOnce2(context, "fern-version-not-file", `Fern version file "${chosen.path}" is not a regular file and was skipped.`);
|
|
4253
4411
|
return config;
|
|
@@ -4485,7 +4643,16 @@ function projectFernNavigation(input) {
|
|
|
4485
4643
|
return section;
|
|
4486
4644
|
}
|
|
4487
4645
|
const distinguishingSegment = section.routeSegments.at(-1);
|
|
4488
|
-
|
|
4646
|
+
let tabLabel = distinguishingSegment ? `${section.tabLabel}: ${titleCase3(distinguishingSegment)}` : section.tabLabel;
|
|
4647
|
+
if (!tabLabel || tabLabel === `${section.tabLabel}: ${section.tabLabel}` || claimedTabLabels.has(tabLabel)) {
|
|
4648
|
+
let ordinal = 2;
|
|
4649
|
+
let candidate = `${section.tabLabel} (${ordinal})`;
|
|
4650
|
+
while (claimedTabLabels.has(candidate)) {
|
|
4651
|
+
ordinal += 1;
|
|
4652
|
+
candidate = `${section.tabLabel} (${ordinal})`;
|
|
4653
|
+
}
|
|
4654
|
+
tabLabel = candidate;
|
|
4655
|
+
}
|
|
4489
4656
|
claimedTabLabels.add(tabLabel);
|
|
4490
4657
|
return { ...section, tabLabel };
|
|
4491
4658
|
});
|
|
@@ -4535,11 +4702,14 @@ var ASSET_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
4535
4702
|
".ico",
|
|
4536
4703
|
".jpeg",
|
|
4537
4704
|
".jpg",
|
|
4705
|
+
".m4a",
|
|
4538
4706
|
".mp3",
|
|
4539
4707
|
".mp4",
|
|
4708
|
+
".ogg",
|
|
4540
4709
|
".pdf",
|
|
4541
4710
|
".png",
|
|
4542
4711
|
".svg",
|
|
4712
|
+
".wav",
|
|
4543
4713
|
".webm",
|
|
4544
4714
|
".webp"
|
|
4545
4715
|
]);
|
|
@@ -4727,9 +4897,92 @@ function readDocusaurusConfigSource(projectRoot) {
|
|
|
4727
4897
|
if (!path || lstatSync5(path).size > 1e6) return "";
|
|
4728
4898
|
return readFileSync5(path, "utf8");
|
|
4729
4899
|
}
|
|
4900
|
+
function matchingBraceIndex(source, openBraceIndex) {
|
|
4901
|
+
let depth = 0;
|
|
4902
|
+
for (let i = openBraceIndex; i < source.length; i++) {
|
|
4903
|
+
const ch = source[i];
|
|
4904
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
4905
|
+
const quote = ch;
|
|
4906
|
+
i++;
|
|
4907
|
+
while (i < source.length && source[i] !== quote) {
|
|
4908
|
+
if (source[i] === "\\") i++;
|
|
4909
|
+
i++;
|
|
4910
|
+
}
|
|
4911
|
+
continue;
|
|
4912
|
+
}
|
|
4913
|
+
if (ch === "/" && source[i + 1] === "/") {
|
|
4914
|
+
const newline = source.indexOf("\n", i);
|
|
4915
|
+
i = newline === -1 ? source.length : newline;
|
|
4916
|
+
continue;
|
|
4917
|
+
}
|
|
4918
|
+
if (ch === "/" && source[i + 1] === "*") {
|
|
4919
|
+
const end = source.indexOf("*/", i + 2);
|
|
4920
|
+
i = end === -1 ? source.length : end + 1;
|
|
4921
|
+
continue;
|
|
4922
|
+
}
|
|
4923
|
+
if (ch === "{") depth++;
|
|
4924
|
+
else if (ch === "}") {
|
|
4925
|
+
depth--;
|
|
4926
|
+
if (depth === 0) return i;
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
return -1;
|
|
4930
|
+
}
|
|
4931
|
+
function topLevelStringProperty(objectBody, key) {
|
|
4932
|
+
let depth = 0;
|
|
4933
|
+
const keyPattern = new RegExp(`^${key}\\s*:\\s*(['"])`);
|
|
4934
|
+
for (let i = 0; i < objectBody.length; i++) {
|
|
4935
|
+
const ch = objectBody[i];
|
|
4936
|
+
if (ch === '"' || ch === "'" || ch === "`") {
|
|
4937
|
+
const quote = ch;
|
|
4938
|
+
i++;
|
|
4939
|
+
while (i < objectBody.length && objectBody[i] !== quote) {
|
|
4940
|
+
if (objectBody[i] === "\\") i++;
|
|
4941
|
+
i++;
|
|
4942
|
+
}
|
|
4943
|
+
continue;
|
|
4944
|
+
}
|
|
4945
|
+
if (ch === "/" && objectBody[i + 1] === "/") {
|
|
4946
|
+
const newline = objectBody.indexOf("\n", i);
|
|
4947
|
+
i = newline === -1 ? objectBody.length : newline;
|
|
4948
|
+
continue;
|
|
4949
|
+
}
|
|
4950
|
+
if (ch === "/" && objectBody[i + 1] === "*") {
|
|
4951
|
+
const end = objectBody.indexOf("*/", i + 2);
|
|
4952
|
+
i = end === -1 ? objectBody.length : end + 1;
|
|
4953
|
+
continue;
|
|
4954
|
+
}
|
|
4955
|
+
if (ch === "{" || ch === "[" || ch === "(") {
|
|
4956
|
+
depth++;
|
|
4957
|
+
continue;
|
|
4958
|
+
}
|
|
4959
|
+
if (ch === "}" || ch === "]" || ch === ")") {
|
|
4960
|
+
depth--;
|
|
4961
|
+
continue;
|
|
4962
|
+
}
|
|
4963
|
+
if (depth === 0) {
|
|
4964
|
+
const match = keyPattern.exec(objectBody.slice(i));
|
|
4965
|
+
if (match) {
|
|
4966
|
+
const quote = match[1];
|
|
4967
|
+
const valueStart = i + match[0].length;
|
|
4968
|
+
const valueEnd = objectBody.indexOf(quote, valueStart);
|
|
4969
|
+
if (valueEnd !== -1) return objectBody.slice(valueStart, valueEnd);
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
}
|
|
4973
|
+
return void 0;
|
|
4974
|
+
}
|
|
4730
4975
|
function primaryDocusaurusDocsDirectory(projectRoot) {
|
|
4731
4976
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
4732
|
-
const
|
|
4977
|
+
const docsKeyMatch = source.match(/\bdocs\s*:\s*\{/);
|
|
4978
|
+
let configured;
|
|
4979
|
+
if (docsKeyMatch?.index !== void 0) {
|
|
4980
|
+
const openBrace = docsKeyMatch.index + docsKeyMatch[0].length - 1;
|
|
4981
|
+
const closeBrace = matchingBraceIndex(source, openBrace);
|
|
4982
|
+
if (closeBrace !== -1) {
|
|
4983
|
+
configured = topLevelStringProperty(source.slice(openBrace + 1, closeBrace), "path");
|
|
4984
|
+
}
|
|
4985
|
+
}
|
|
4733
4986
|
return trimTrailingSlashes(configured?.replace(/^\.\//, "") ?? "") || "docs";
|
|
4734
4987
|
}
|
|
4735
4988
|
function docusaurusArchiveRoots(projectRoot, repositoryDir, warnings) {
|
|
@@ -4781,7 +5034,7 @@ function docusaurusArchiveRoots(projectRoot, repositoryDir, warnings) {
|
|
|
4781
5034
|
function additionalDocusaurusPluginRoots(repositoryDir, projectRoot, warnings) {
|
|
4782
5035
|
const source = readDocusaurusConfigSource(projectRoot);
|
|
4783
5036
|
const plugins = [];
|
|
4784
|
-
const matcher = /['"]
|
|
5037
|
+
const matcher = /['"](?:@docusaurus\/plugin-)?content-docs['"][\s\S]{0,3000}?\bpath\s*:\s*(['"])([^'"]+)\1[\s\S]{0,1000}?\brouteBasePath\s*:\s*(['"])([^'"]+)\3/g;
|
|
4785
5038
|
for (const match of source.matchAll(matcher)) {
|
|
4786
5039
|
const localPath = trimTrailingSlashes(match[2].replace(/^\.\//, ""));
|
|
4787
5040
|
const routePrefix = trimEdgeSlashes(match[4]);
|
|
@@ -5019,8 +5272,10 @@ function detectRepositoryDocsDir(repositoryDir) {
|
|
|
5019
5272
|
}
|
|
5020
5273
|
return "";
|
|
5021
5274
|
}
|
|
5022
|
-
|
|
5275
|
+
var MAX_RANKED_WALK_FILES = MAX_SOURCE_FILES * 20;
|
|
5276
|
+
function scanFiles(root, confinementRoot = root, warnings, rank) {
|
|
5023
5277
|
const files = [];
|
|
5278
|
+
const walkCap = rank ? MAX_RANKED_WALK_FILES : MAX_SOURCE_FILES;
|
|
5024
5279
|
let confinementReal;
|
|
5025
5280
|
try {
|
|
5026
5281
|
confinementReal = realpathSync2(confinementRoot);
|
|
@@ -5029,7 +5284,7 @@ function scanFiles(root, confinementRoot = root, warnings) {
|
|
|
5029
5284
|
}
|
|
5030
5285
|
const visitedRealPaths = /* @__PURE__ */ new Set();
|
|
5031
5286
|
function visit(directory, logicalDirectory = directory) {
|
|
5032
|
-
if (files.length >=
|
|
5287
|
+
if (files.length >= walkCap) return;
|
|
5033
5288
|
let directoryReal;
|
|
5034
5289
|
try {
|
|
5035
5290
|
directoryReal = realpathSync2(directory);
|
|
@@ -5045,7 +5300,7 @@ function scanFiles(root, confinementRoot = root, warnings) {
|
|
|
5045
5300
|
return;
|
|
5046
5301
|
}
|
|
5047
5302
|
for (const entry of entries) {
|
|
5048
|
-
if (files.length >=
|
|
5303
|
+
if (files.length >= walkCap) return;
|
|
5049
5304
|
if (isIgnoredContentDirectory(entry.name)) {
|
|
5050
5305
|
if (warnings && entry.isDirectory() && !entry.isSymbolicLink() && containsMarkdown(resolveWithin(directory, entry.name))) {
|
|
5051
5306
|
warnings.push({
|
|
@@ -5082,8 +5337,44 @@ function scanFiles(root, confinementRoot = root, warnings) {
|
|
|
5082
5337
|
}
|
|
5083
5338
|
}
|
|
5084
5339
|
visit(root);
|
|
5340
|
+
if (rank && files.length >= walkCap) {
|
|
5341
|
+
warnings?.push({
|
|
5342
|
+
code: "limit-reached",
|
|
5343
|
+
message: `Stopped scanning after ${walkCap} files, so the rest of the repository was not looked at. Run the migration on a smaller part of the repository with --docs-dir.`
|
|
5344
|
+
});
|
|
5345
|
+
}
|
|
5085
5346
|
return files;
|
|
5086
5347
|
}
|
|
5348
|
+
function selectGroupWithinBudget(scanned, rank, warnings, allVersionPrefixes, label) {
|
|
5349
|
+
if (scanned.length <= MAX_SOURCE_FILES) return scanned;
|
|
5350
|
+
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));
|
|
5351
|
+
const dropped = ranked.slice(MAX_SOURCE_FILES).map(({ file }) => file);
|
|
5352
|
+
if (warnings) {
|
|
5353
|
+
const droppedPages = label === "file" ? dropped.filter((file) => isDocumentationExtension(file.relativePath)) : dropped;
|
|
5354
|
+
const noun = label === "file" ? "page" : "image or media file";
|
|
5355
|
+
const droppedVersions = /* @__PURE__ */ new Set();
|
|
5356
|
+
for (const file of dropped) {
|
|
5357
|
+
const firstSegment = file.relativePath.split("/", 1)[0];
|
|
5358
|
+
if (allVersionPrefixes.has(firstSegment)) droppedVersions.add(firstSegment);
|
|
5359
|
+
}
|
|
5360
|
+
const examples = droppedPages.slice(0, 3).map((file) => file.relativePath);
|
|
5361
|
+
const rest = droppedPages.length - examples.length;
|
|
5362
|
+
warnings.push({
|
|
5363
|
+
code: "limit-reached",
|
|
5364
|
+
message: `This repository has more than ${MAX_SOURCE_FILES} ${label === "file" ? "files" : "assets"}, so only the first ${MAX_SOURCE_FILES} (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.")
|
|
5365
|
+
});
|
|
5366
|
+
}
|
|
5367
|
+
return ranked.slice(0, MAX_SOURCE_FILES).map(({ file }) => file);
|
|
5368
|
+
}
|
|
5369
|
+
function selectFilesWithinBudget(scanned, rank, warnings, allVersionPrefixes) {
|
|
5370
|
+
const isAsset = (file) => ASSET_EXTENSIONS.has(extname5(file.relativePath).toLowerCase());
|
|
5371
|
+
const assetFiles = scanned.filter(isAsset);
|
|
5372
|
+
const otherFiles = scanned.filter((file) => !isAsset(file));
|
|
5373
|
+
return [
|
|
5374
|
+
...selectGroupWithinBudget(otherFiles, rank, warnings, allVersionPrefixes, "file"),
|
|
5375
|
+
...selectGroupWithinBudget(assetFiles, rank, warnings, allVersionPrefixes, "asset")
|
|
5376
|
+
];
|
|
5377
|
+
}
|
|
5087
5378
|
function readMintignoreMatcher(mintlifyRoot) {
|
|
5088
5379
|
const path = resolveWithin(mintlifyRoot, MINTIGNORE_FILENAME);
|
|
5089
5380
|
if (!existsSync5(path) || !lstatSync5(path).isFile()) return null;
|
|
@@ -5200,10 +5491,159 @@ function resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteSpecs) {
|
|
|
5200
5491
|
});
|
|
5201
5492
|
continue;
|
|
5202
5493
|
}
|
|
5203
|
-
|
|
5494
|
+
if (reference.directory) {
|
|
5495
|
+
warnings.push({
|
|
5496
|
+
code: "unsupported-config",
|
|
5497
|
+
message: `The OpenAPI spec "${reference.value}"${tabSuffix} was limited to pages under "${reference.directory}" in the source, but Thally's API reference always covers a whole tab, so it was migrated as the tab's full API reference. Update any links to "${reference.directory}/..." pages manually.`
|
|
5498
|
+
});
|
|
5499
|
+
}
|
|
5500
|
+
specs.push({
|
|
5501
|
+
filename: basename3(match.relativePath),
|
|
5502
|
+
content: readFileSync5(match.absolutePath),
|
|
5503
|
+
tabLabel: reference.tabLabel,
|
|
5504
|
+
sourcePath: match.relativePath,
|
|
5505
|
+
directory: reference.directory
|
|
5506
|
+
});
|
|
5507
|
+
}
|
|
5508
|
+
const filenameOwners = /* @__PURE__ */ new Map();
|
|
5509
|
+
for (const spec of specs) {
|
|
5510
|
+
const groupKey = spec.tabLabel ?? "\0default";
|
|
5511
|
+
const owner = filenameOwners.get(spec.filename);
|
|
5512
|
+
if (owner !== void 0 && owner !== groupKey) {
|
|
5513
|
+
let disambiguated = spec.sourcePath.replace(/\//g, "-");
|
|
5514
|
+
while (filenameOwners.has(disambiguated) && filenameOwners.get(disambiguated) !== groupKey) {
|
|
5515
|
+
disambiguated = `${groupKey.replace(/\W+/g, "-")}-${disambiguated}`;
|
|
5516
|
+
}
|
|
5517
|
+
spec.filename = disambiguated;
|
|
5518
|
+
}
|
|
5519
|
+
filenameOwners.set(spec.filename, groupKey);
|
|
5204
5520
|
}
|
|
5205
5521
|
return specs;
|
|
5206
5522
|
}
|
|
5523
|
+
var OPENAPI_HTTP_METHODS = ["get", "post", "put", "patch", "delete", "options", "head", "trace"];
|
|
5524
|
+
var FERN_CRUD_METHOD_NAMES = {
|
|
5525
|
+
findall: "list",
|
|
5526
|
+
findone: "get",
|
|
5527
|
+
remove: "delete"
|
|
5528
|
+
};
|
|
5529
|
+
function mintlifyOperationSlugSegment(value) {
|
|
5530
|
+
return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
|
|
5531
|
+
}
|
|
5532
|
+
function thallyOperationSlugSegments(path, method) {
|
|
5533
|
+
const cleaned = path.split("/").filter(Boolean).map((segment) => segment.replace(/[{}]/g, "").replace(/[^a-zA-Z0-9-]/g, "-").replace(/-+/g, "-").toLowerCase());
|
|
5534
|
+
if (!cleaned.length) cleaned.push("root");
|
|
5535
|
+
cleaned.push(method.toLowerCase());
|
|
5536
|
+
return cleaned;
|
|
5537
|
+
}
|
|
5538
|
+
function slugifyApiSpecId(value) {
|
|
5539
|
+
return value.toLowerCase().replace(/[^a-z0-9/]+/g, "-").replace(/(^-|-$)+/g, "").replace(/\//g, "-") || value.toLowerCase();
|
|
5540
|
+
}
|
|
5541
|
+
function parseOpenApiOperationsForLinking(content) {
|
|
5542
|
+
let document;
|
|
5543
|
+
try {
|
|
5544
|
+
document = parseYaml4(content.toString("utf8"));
|
|
5545
|
+
} catch {
|
|
5546
|
+
return [];
|
|
5547
|
+
}
|
|
5548
|
+
const paths = document && typeof document === "object" ? document.paths : null;
|
|
5549
|
+
if (!paths || typeof paths !== "object") return [];
|
|
5550
|
+
const operations = [];
|
|
5551
|
+
for (const [path, pathItem] of Object.entries(paths)) {
|
|
5552
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
5553
|
+
for (const method of OPENAPI_HTTP_METHODS) {
|
|
5554
|
+
const operation = pathItem[method];
|
|
5555
|
+
if (!operation || typeof operation !== "object") continue;
|
|
5556
|
+
const op = operation;
|
|
5557
|
+
const tags = Array.isArray(op.tags) ? op.tags.filter((tag) => typeof tag === "string") : [];
|
|
5558
|
+
operations.push({
|
|
5559
|
+
method,
|
|
5560
|
+
path,
|
|
5561
|
+
tag: tags[0],
|
|
5562
|
+
summary: typeof op.summary === "string" ? op.summary : void 0,
|
|
5563
|
+
operationId: typeof op.operationId === "string" ? op.operationId : void 0
|
|
5564
|
+
});
|
|
5565
|
+
}
|
|
5566
|
+
}
|
|
5567
|
+
return operations;
|
|
5568
|
+
}
|
|
5569
|
+
function apiOperationLinkMap(sources, docsConfig) {
|
|
5570
|
+
const map = /* @__PURE__ */ new Map();
|
|
5571
|
+
const prefixLandings = /* @__PURE__ */ new Map();
|
|
5572
|
+
const apiTabs = docsConfig.tabs.filter((tab) => !tab.hidden && tab.api);
|
|
5573
|
+
apiTabs.forEach((tab, index) => {
|
|
5574
|
+
const source = sources.find((entry) => tab.api?.source === `/${entry.filename}`);
|
|
5575
|
+
if (!source || !source.prefix) return;
|
|
5576
|
+
const specId = index === 0 ? "default" : slugifyApiSpecId(tab.tab);
|
|
5577
|
+
const prefix = source.prefix.replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
5578
|
+
if (prefix) prefixLandings.set(prefix, specId === "default" ? "/api" : `/api/${specId}`);
|
|
5579
|
+
for (const operation of parseOpenApiOperationsForLinking(source.content)) {
|
|
5580
|
+
const thallyHref = `/api/${specId}/${thallyOperationSlugSegments(operation.path, operation.method).join("/")}`;
|
|
5581
|
+
const leafCandidates = /* @__PURE__ */ new Set();
|
|
5582
|
+
if (operation.operationId) {
|
|
5583
|
+
const parts = operation.operationId.split(/[_.]/).filter(Boolean);
|
|
5584
|
+
const lastPart = parts.at(-1) ?? operation.operationId;
|
|
5585
|
+
const conventional = FERN_CRUD_METHOD_NAMES[lastPart.toLowerCase()];
|
|
5586
|
+
if (conventional) leafCandidates.add(conventional);
|
|
5587
|
+
leafCandidates.add(mintlifyOperationSlugSegment(lastPart));
|
|
5588
|
+
}
|
|
5589
|
+
if (operation.summary) leafCandidates.add(mintlifyOperationSlugSegment(operation.summary));
|
|
5590
|
+
if (operation.operationId) leafCandidates.add(mintlifyOperationSlugSegment(operation.operationId));
|
|
5591
|
+
leafCandidates.add(mintlifyOperationSlugSegment(`${operation.method} ${operation.path}`));
|
|
5592
|
+
const tagSegment = mintlifyOperationSlugSegment(operation.tag ?? "default");
|
|
5593
|
+
for (const leaf of leafCandidates) {
|
|
5594
|
+
if (!leaf) continue;
|
|
5595
|
+
const tagged = `${source.prefix}/${tagSegment}/${leaf}`.replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
5596
|
+
const flat = `${source.prefix}/${leaf}`.replace(/^\/+|\/+$/g, "").toLowerCase();
|
|
5597
|
+
if (!map.has(tagged)) map.set(tagged, thallyHref);
|
|
5598
|
+
if (!map.has(flat)) map.set(flat, thallyHref);
|
|
5599
|
+
}
|
|
5600
|
+
}
|
|
5601
|
+
});
|
|
5602
|
+
return { operationLinks: map, prefixLandings };
|
|
5603
|
+
}
|
|
5604
|
+
function rewriteApiLinksInPages(pages, operationLinks, prefixLandings, warnings) {
|
|
5605
|
+
if (operationLinks.size === 0 && prefixLandings.size === 0) return;
|
|
5606
|
+
const unmatched = /* @__PURE__ */ new Set();
|
|
5607
|
+
for (const page of pages) {
|
|
5608
|
+
page.body = rewriteApiOperationLinks(page.body, operationLinks, prefixLandings, unmatched);
|
|
5609
|
+
}
|
|
5610
|
+
if (unmatched.size === 0) return;
|
|
5611
|
+
const sorted = [...unmatched].sort();
|
|
5612
|
+
warnings.push({
|
|
5613
|
+
code: "unsupported-config",
|
|
5614
|
+
message: `${sorted.length} link(s) to API endpoints matched no endpoint in the OpenAPI spec, so they now point to the API section's overview page instead: ${sorted.slice(0, 10).join(", ")}${sorted.length > 10 ? `, and ${sorted.length - 10} more` : ""}. Update those links if you want them to reach a specific endpoint.`
|
|
5615
|
+
});
|
|
5616
|
+
}
|
|
5617
|
+
function rewriteApiOperationLinks(body, linkMap, prefixLandings, unmatched) {
|
|
5618
|
+
if (linkMap.size === 0 && prefixLandings.size === 0) return body;
|
|
5619
|
+
const orderedPrefixes = [...prefixLandings.entries()].sort((a, b) => b[0].length - a[0].length);
|
|
5620
|
+
let codeFence = null;
|
|
5621
|
+
return body.split("\n").map((line) => {
|
|
5622
|
+
const fence = line.match(/^\s*(`{3,}|~{3,})/);
|
|
5623
|
+
if (fence) {
|
|
5624
|
+
if (!codeFence) codeFence = fence[1][0];
|
|
5625
|
+
else if (fence[1][0] === codeFence) codeFence = null;
|
|
5626
|
+
return line;
|
|
5627
|
+
}
|
|
5628
|
+
if (codeFence) return line;
|
|
5629
|
+
const rewriteTarget = (target) => {
|
|
5630
|
+
const suffixIndex = target.search(/[?#]/);
|
|
5631
|
+
const path = (suffixIndex >= 0 ? target.slice(0, suffixIndex) : target).replace(/^\/+/, "").toLowerCase();
|
|
5632
|
+
const suffix = suffixIndex >= 0 ? target.slice(suffixIndex) : "";
|
|
5633
|
+
const mapped = linkMap.get(path);
|
|
5634
|
+
if (mapped) return `${mapped}${suffix}`;
|
|
5635
|
+
for (const [prefix, landing] of orderedPrefixes) {
|
|
5636
|
+
if (path === prefix) return `${landing}${suffix}`;
|
|
5637
|
+
if (path.startsWith(`${prefix}/`)) {
|
|
5638
|
+
unmatched.add(target);
|
|
5639
|
+
return `${landing}${suffix}`;
|
|
5640
|
+
}
|
|
5641
|
+
}
|
|
5642
|
+
return target;
|
|
5643
|
+
};
|
|
5644
|
+
return line.replace(/(\]\()\/([^\s)]+)(?=[\s)]|$)/g, (_match, prefix, target) => `${prefix}${rewriteTarget(`/${target}`)}`).replace(/(\bhref=")\/([^"]+)(")/g, (_match, prefix, target, suffix) => `${prefix}${rewriteTarget(`/${target}`)}${suffix}`);
|
|
5645
|
+
}).join("\n");
|
|
5646
|
+
}
|
|
5207
5647
|
var MAX_FERN_GENERATORS_BYTES = 2e6;
|
|
5208
5648
|
function fernGeneratorsOpenApiPaths(config) {
|
|
5209
5649
|
const api = config.api;
|
|
@@ -5820,17 +6260,27 @@ ${result.slice(prefix.length)}`;
|
|
|
5820
6260
|
}
|
|
5821
6261
|
return depth === 0 ? hoistMdxImports(result) : result;
|
|
5822
6262
|
}
|
|
5823
|
-
function injectOpenApiSpecs(config, specs) {
|
|
6263
|
+
function injectOpenApiSpecs(config, specs, warnings) {
|
|
5824
6264
|
let tabs = config.tabs.map((tab) => ({ ...tab }));
|
|
5825
6265
|
for (const spec of specs) {
|
|
5826
6266
|
const apiTab = spec.tabLabel ? tabs.find((tab) => tab.tab === spec.tabLabel) : tabs.find((tab) => tab.tab.toLowerCase().includes("api"));
|
|
5827
|
-
if (apiTab)
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
6267
|
+
if (apiTab) {
|
|
6268
|
+
if (apiTab.api && warnings) {
|
|
6269
|
+
warnings.push({
|
|
6270
|
+
code: "unsupported-config",
|
|
6271
|
+
message: `Tab "${apiTab.tab}" already uses another OpenAPI spec, and Thally supports one API spec per tab, so "/${spec.filename}" was not added. Put it in its own tab to include it.`
|
|
6272
|
+
});
|
|
6273
|
+
continue;
|
|
6274
|
+
}
|
|
6275
|
+
apiTab.api = {
|
|
6276
|
+
source: `/${spec.filename}`,
|
|
6277
|
+
// An API-only tab needs generated endpoint navigation; an authored
|
|
6278
|
+
// page tab keeps its own groups alongside the bound spec.
|
|
6279
|
+
...apiTab.groups?.length || apiTab.pages?.length ? { navigation: false } : {}
|
|
6280
|
+
};
|
|
6281
|
+
} else {
|
|
6282
|
+
tabs = [...tabs, { tab: spec.tabLabel ?? "API Reference", api: { source: `/${spec.filename}` } }];
|
|
6283
|
+
}
|
|
5834
6284
|
}
|
|
5835
6285
|
return { ...config, tabs };
|
|
5836
6286
|
}
|
|
@@ -5870,6 +6320,8 @@ function migrateRepository(options) {
|
|
|
5870
6320
|
const componentRoot = mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir;
|
|
5871
6321
|
const componentMigrator = platform === "mintlify" || platform === "docusaurus" || platform === "fern" ? createComponentMigrator(componentRoot, repositoryDir, warnings, componentSourceIdentity(options.sourceUrl, repositoryDir, componentRoot)) : void 0;
|
|
5872
6322
|
let docsConfig = { tabs: [] };
|
|
6323
|
+
let defaultVersionPrefixes = /* @__PURE__ */ new Set();
|
|
6324
|
+
let allVersionPrefixes = /* @__PURE__ */ new Set();
|
|
5873
6325
|
const referenceMap = /* @__PURE__ */ new Map();
|
|
5874
6326
|
const exactReferenceMap = /* @__PURE__ */ new Map();
|
|
5875
6327
|
const referenceOrder = /* @__PURE__ */ new Map();
|
|
@@ -5879,6 +6331,7 @@ function migrateRepository(options) {
|
|
|
5879
6331
|
let fernBasePath = "";
|
|
5880
6332
|
let fernVersionPath = "";
|
|
5881
6333
|
let fernApiSections = [];
|
|
6334
|
+
const fernExternalSourcePaths = /* @__PURE__ */ new Set();
|
|
5882
6335
|
let fernChangelogIndexes = [];
|
|
5883
6336
|
const fernReferencedPaths = /* @__PURE__ */ new Set();
|
|
5884
6337
|
const fernSourceLinkAliases = /* @__PURE__ */ new Map();
|
|
@@ -5889,6 +6342,8 @@ function migrateRepository(options) {
|
|
|
5889
6342
|
const config = readMintlifyConfig(mintlifyProjectRoot ?? repositoryDir);
|
|
5890
6343
|
if (config) {
|
|
5891
6344
|
mintlifyConfig = config;
|
|
6345
|
+
defaultVersionPrefixes = mintlifyDefaultVersionPrefixes(config);
|
|
6346
|
+
allVersionPrefixes = mintlifyAllVersionPrefixes(config);
|
|
5892
6347
|
const projected = projectMintlifyNavigation(config);
|
|
5893
6348
|
docsConfig = projected.docsConfig;
|
|
5894
6349
|
warnings.push(...projected.warnings);
|
|
@@ -5939,6 +6394,7 @@ function migrateRepository(options) {
|
|
|
5939
6394
|
const exactKey = exactReferenceKey(descriptor.sourcePath);
|
|
5940
6395
|
if (!exactReferenceMap.has(exactKey)) exactReferenceMap.set(exactKey, { navigationId: descriptor.navigationId });
|
|
5941
6396
|
if (!referenceOrder.has(key)) referenceOrder.set(key, index);
|
|
6397
|
+
if (descriptor.sourcePath.startsWith("../")) fernExternalSourcePaths.add(descriptor.sourcePath);
|
|
5942
6398
|
}
|
|
5943
6399
|
}
|
|
5944
6400
|
} catch (error) {
|
|
@@ -5963,15 +6419,35 @@ function migrateRepository(options) {
|
|
|
5963
6419
|
throw new Error(`Documentation directory does not exist: ${configuredDocsDir || "."}`);
|
|
5964
6420
|
}
|
|
5965
6421
|
const mintignoreMatcher = platform === "mintlify" && mintlifyProjectRoot ? readMintignoreMatcher(mintlifyProjectRoot) : null;
|
|
5966
|
-
const
|
|
6422
|
+
const lastReferencedIndexByVersionLocale = /* @__PURE__ */ new Map();
|
|
6423
|
+
for (const [key, index] of referenceOrder) {
|
|
6424
|
+
const blockKey = key.split("/").slice(0, 2).join("/");
|
|
6425
|
+
const current = lastReferencedIndexByVersionLocale.get(blockKey);
|
|
6426
|
+
if (current === void 0 || index > current) lastReferencedIndexByVersionLocale.set(blockKey, index);
|
|
6427
|
+
}
|
|
6428
|
+
const discoveryRank = referenceOrder.size > 0 ? (relativePath) => {
|
|
6429
|
+
const referencedIndex = referenceOrder.get(normalizedReferenceKey(relativePath));
|
|
6430
|
+
if (referencedIndex !== void 0) return referencedIndex;
|
|
6431
|
+
const blockKey = relativePath.split("/").slice(0, 2).join("/");
|
|
6432
|
+
const versionCeiling = lastReferencedIndexByVersionLocale.get(blockKey);
|
|
6433
|
+
return versionCeiling !== void 0 ? versionCeiling + 1 : Number.MAX_SAFE_INTEGER;
|
|
6434
|
+
} : void 0;
|
|
6435
|
+
const scannedFiles = scanFiles(contentRoot, repositoryDir, warnings, discoveryRank);
|
|
6436
|
+
const mintignoreFilteredFiles = mintignoreMatcher ? scannedFiles.filter((file) => !mintignoreMatcher.ignores(file.relativePath)) : scannedFiles;
|
|
6437
|
+
const discoveryBudgetApplied = discoveryRank !== void 0 && mintignoreFilteredFiles.length > MAX_SOURCE_FILES;
|
|
6438
|
+
const files = discoveryRank ? selectFilesWithinBudget(mintignoreFilteredFiles, discoveryRank, warnings, allVersionPrefixes) : mintignoreFilteredFiles;
|
|
5967
6439
|
if (platform === "fern" && fernProjectRoot) {
|
|
5968
|
-
const
|
|
5969
|
-
for (const sourcePath of
|
|
6440
|
+
const discovered2 = new Set(files.map((file) => file.relativePath));
|
|
6441
|
+
for (const sourcePath of fernExternalSourcePaths) {
|
|
6442
|
+
if (discovered2.has(sourcePath)) continue;
|
|
5970
6443
|
try {
|
|
5971
6444
|
const absolutePath = resolveWithinRoot(fernProjectRoot, sourcePath, repositoryDir);
|
|
5972
|
-
if (
|
|
5973
|
-
|
|
5974
|
-
|
|
6445
|
+
if (existsSync5(absolutePath) && lstatSync5(absolutePath).isFile()) {
|
|
6446
|
+
files.push({ absolutePath, relativePath: sourcePath });
|
|
6447
|
+
discovered2.add(sourcePath);
|
|
6448
|
+
} else {
|
|
6449
|
+
warnings.push({ code: "missing-page", message: "A Fern navigation path could not be read within this repository.", source: sourcePath });
|
|
6450
|
+
}
|
|
5975
6451
|
} catch {
|
|
5976
6452
|
warnings.push({ code: "missing-page", message: "A Fern navigation path could not be read within this repository.", source: sourcePath });
|
|
5977
6453
|
}
|
|
@@ -6057,6 +6533,9 @@ function migrateRepository(options) {
|
|
|
6057
6533
|
mintlifyProjectRoot ?? docusaurusProjectRoot ?? fernProjectRoot ?? repositoryDir,
|
|
6058
6534
|
snippetAliases
|
|
6059
6535
|
);
|
|
6536
|
+
if (platform === "fern" || platform === "mintlify" || platform === "docusaurus") {
|
|
6537
|
+
raw = normalizeExplicitHeadingIds(raw);
|
|
6538
|
+
}
|
|
6060
6539
|
if (componentMigrator) {
|
|
6061
6540
|
const warningsBeforeTransform = warnings.length;
|
|
6062
6541
|
raw = componentMigrator.transform(raw, file.absolutePath);
|
|
@@ -6132,6 +6611,13 @@ function migrateRepository(options) {
|
|
|
6132
6611
|
skipped++;
|
|
6133
6612
|
continue;
|
|
6134
6613
|
}
|
|
6614
|
+
if (page.frontmatterError) {
|
|
6615
|
+
warnings.push({
|
|
6616
|
+
code: "unsupported-config",
|
|
6617
|
+
message: `The page's frontmatter is not valid YAML (${page.frontmatterError}); the page was kept and as much of its metadata as could be read was used.`,
|
|
6618
|
+
source: relative5(repositoryDir, file.absolutePath).replace(/\\/g, "/")
|
|
6619
|
+
});
|
|
6620
|
+
}
|
|
6135
6621
|
if (platform === "fern" && !page.navTitle) {
|
|
6136
6622
|
const navTitle = fernNavTitles.get(navigationId);
|
|
6137
6623
|
if (navTitle && navTitle !== page.title) page.navTitle = navTitle;
|
|
@@ -6211,6 +6697,13 @@ function migrateRepository(options) {
|
|
|
6211
6697
|
if (sourcePath !== page.id && /^[A-Za-z0-9_./-]+$/.test(sourcePath)) {
|
|
6212
6698
|
routeAliases.push({ source: `/${sourcePath}`, destination: `/${page.id}`, permanent: false });
|
|
6213
6699
|
}
|
|
6700
|
+
const versionPrefix = [...defaultVersionPrefixes].find((prefix) => sourcePath === prefix || sourcePath.startsWith(`${prefix}/`));
|
|
6701
|
+
if (versionPrefix) {
|
|
6702
|
+
const unversioned = sourcePath.slice(versionPrefix.length).replace(/^\/+/, "");
|
|
6703
|
+
if (unversioned && unversioned !== sourcePath && unversioned !== page.id && /^[A-Za-z0-9_./-]+$/.test(unversioned) && !routeAliases.some((redirect) => redirect.source === `/${unversioned}`)) {
|
|
6704
|
+
routeAliases.push({ source: `/${unversioned}`, destination: `/${page.id}`, permanent: false });
|
|
6705
|
+
}
|
|
6706
|
+
}
|
|
6214
6707
|
}
|
|
6215
6708
|
if (docusaurusDescriptor) {
|
|
6216
6709
|
docusaurusDescriptors.push({ ...docusaurusDescriptor, title: page.title });
|
|
@@ -6289,7 +6782,7 @@ function migrateRepository(options) {
|
|
|
6289
6782
|
const referencingPages = referencedAssetPaths.get(assetPath);
|
|
6290
6783
|
warnings.push({
|
|
6291
6784
|
code: "limit-reached",
|
|
6292
|
-
message:
|
|
6785
|
+
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` : ""}.` : "."),
|
|
6293
6786
|
source: file.relativePath
|
|
6294
6787
|
});
|
|
6295
6788
|
continue;
|
|
@@ -6306,8 +6799,8 @@ function migrateRepository(options) {
|
|
|
6306
6799
|
assets.push({ path: assetPath, content });
|
|
6307
6800
|
totalAssetBytes += size;
|
|
6308
6801
|
}
|
|
6309
|
-
if (files.length >= MAX_SOURCE_FILES) {
|
|
6310
|
-
warnings.push({ code: "limit-reached", message: `
|
|
6802
|
+
if (!discoveryBudgetApplied && files.length >= MAX_SOURCE_FILES) {
|
|
6803
|
+
warnings.push({ code: "limit-reached", message: `Stopped scanning after ${MAX_SOURCE_FILES} files, so the rest of the repository was not looked at. Run the migration on a smaller part of the repository with --docs-dir.` });
|
|
6311
6804
|
}
|
|
6312
6805
|
if (platform === "docusaurus") {
|
|
6313
6806
|
const projected = projectDocusaurusNavigation({
|
|
@@ -6393,10 +6886,11 @@ function migrateRepository(options) {
|
|
|
6393
6886
|
filename = `${prefix}${filename}`;
|
|
6394
6887
|
}
|
|
6395
6888
|
specFilenameSources.set(filename, spec.absolutePath);
|
|
6889
|
+
const specContent = readFileSync5(spec.absolutePath);
|
|
6396
6890
|
if (!assets.some((asset) => asset.path === filename)) {
|
|
6397
|
-
assets.push({ path: filename, content:
|
|
6891
|
+
assets.push({ path: filename, content: specContent });
|
|
6398
6892
|
}
|
|
6399
|
-
resolvedSpecs.push({ filename, tabLabel: section.tabLabel });
|
|
6893
|
+
resolvedSpecs.push({ filename, tabLabel: section.tabLabel, content: specContent, routeSegments: section.routeSegments ?? [] });
|
|
6400
6894
|
continue;
|
|
6401
6895
|
}
|
|
6402
6896
|
if (resolution.unsupported.length > 0) {
|
|
@@ -6424,7 +6918,14 @@ function migrateRepository(options) {
|
|
|
6424
6918
|
message: checked.length ? `No OpenAPI/AsyncAPI spec could be found for the "${section.name}" API. Checked: ${checked.join(", ")}. Point generators.yml at a spec file that exists.` : `No OpenAPI/AsyncAPI spec could be found for the "${section.name}" API, because its api-name is not a valid folder name. Add the spec manually.`
|
|
6425
6919
|
});
|
|
6426
6920
|
}
|
|
6427
|
-
if (resolvedSpecs.length > 0)
|
|
6921
|
+
if (resolvedSpecs.length > 0) {
|
|
6922
|
+
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs, warnings);
|
|
6923
|
+
const { operationLinks, prefixLandings } = apiOperationLinkMap(
|
|
6924
|
+
resolvedSpecs.map((spec) => ({ filename: spec.filename, content: spec.content, prefix: spec.routeSegments.join("/") })),
|
|
6925
|
+
docsConfig
|
|
6926
|
+
);
|
|
6927
|
+
rewriteApiLinksInPages(pages, operationLinks, prefixLandings, warnings);
|
|
6928
|
+
}
|
|
6428
6929
|
} else if (platform === "mintlify") {
|
|
6429
6930
|
const resolvedSpecs = resolveMintlifyApiSpecs(mintlifyConfig, files, warnings, remoteApiSpecs);
|
|
6430
6931
|
for (const spec of resolvedSpecs) {
|
|
@@ -6433,7 +6934,12 @@ function migrateRepository(options) {
|
|
|
6433
6934
|
}
|
|
6434
6935
|
}
|
|
6435
6936
|
if (resolvedSpecs.length > 0) {
|
|
6436
|
-
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs);
|
|
6937
|
+
docsConfig = injectOpenApiSpecs(docsConfig, resolvedSpecs, warnings);
|
|
6938
|
+
const { operationLinks, prefixLandings } = apiOperationLinkMap(
|
|
6939
|
+
resolvedSpecs.map((spec) => ({ filename: spec.filename, content: spec.content, prefix: spec.directory })),
|
|
6940
|
+
docsConfig
|
|
6941
|
+
);
|
|
6942
|
+
rewriteApiLinksInPages(pages, operationLinks, prefixLandings, warnings);
|
|
6437
6943
|
} else {
|
|
6438
6944
|
const fallback = findOpenApi(files);
|
|
6439
6945
|
if (fallback) {
|
|
@@ -6466,7 +6972,7 @@ function migrateRepository(options) {
|
|
|
6466
6972
|
];
|
|
6467
6973
|
for (const plugin of additionalRoots) {
|
|
6468
6974
|
if (discovered >= MAX_SOURCE_FILES) {
|
|
6469
|
-
warnings.push({ code: "limit-reached", message: `Docusaurus docs
|
|
6975
|
+
warnings.push({ code: "limit-reached", message: `Stopped importing Docusaurus docs plugins after ${MAX_SOURCE_FILES} files; the remaining plugin docs were not migrated.` });
|
|
6470
6976
|
break;
|
|
6471
6977
|
}
|
|
6472
6978
|
if (plugin.docsDir === configuredDocsDir) continue;
|